/* tls13.c * * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include /* * TLS 1.3-Specific Build Options: * (See tls.c for generic TLS options: extensions, curves, callbacks, etc.) * * Protocol: * WOLFSSL_TLS13: Enable TLS 1.3 protocol default: on * WOLFSSL_TLS13_DRAFT: Enable TLS 1.3 draft version support default: off * WOLFSSL_QUIC: Enable QUIC protocol support (TLS 1.3) default: off * WOLFSSL_DTLS13_NO_HRR_ON_RESUME: Skip HRR on DTLS 1.3 resume default: off * WOLFSSL_DTLS_CH_FRAG: Enable DTLS 1.3 ClientHello frag default: off * * Handshake: * WOLFSSL_TLS13_MIDDLEBOX_COMPAT: Client-side middlebox compatibility * default: off * Makes the client send a fake session id and its * own ChangeCipherSpec. The server always answers * a non-empty client session id with a * ChangeCipherSpec, as RFC 8446 Appendix D.4 * requires, whether or not this is defined. * WOLFSSL_SEND_HRR_COOKIE: Send cookie in HelloRetryRequest default: off * for stateless ClientHello tracking. A client * always echoes back a cookie it is sent. * WOLFSSL_MAX_TLS13_COOKIE_SZ: Largest cookie a client accepts default: 4096 * in a HelloRetryRequest. * WOLFSSL_EARLY_DATA: Allow 0-RTT early data default: off * WOLFSSL_EARLY_DATA_GROUP: Group early data with ClientHello default: off * WOLFSSL_POST_HANDSHAKE_AUTH: Post-handshake client auth default: off * WOLFSSL_TLS13_TICKET_BEFORE_FINISHED: Send NewSessionTicket default: off * before client Finished message. Violates the * RFC 8446 Section 4.6.1 ordering requirement; for * interop with peers that expect the early ticket. * WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID: Echo legacy_session_id default: off * in DTLS 1.3. Violates RFC 9147 Section 5 ("DTLS * servers MUST NOT echo the legacy_session_id * value from the client"). A server built with this * option echoes the session ID, which a compliant * client rejects, so enable it only where the peer * is wolfSSL <= 5.9.0 or shares this option. * WOLFSSL_NO_CLIENT_AUTH: Disable TLS 1.3 client authentication default: off * WOLFSSL_NO_CLIENT_CERT_ERROR: Require client certificate default: off * WOLFSSL_CERT_SETUP_CB: Certificate setup callback default: off * WOLFSSL_ALLOW_BAD_TLS_LEGACY_VERSION: Allow bad legacy version default: off * * Security: * WOLFSSL_BLIND_PRIVATE_KEY: Blind private key during signing default: off * WOLFSSL_CHECK_SIG_FAULTS: Verify signature after ECC signing default: off * to detect fault injection attacks * WOLFSSL_CIPHER_TEXT_CHECK: Verify ciphertext integrity default: off * WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT: Include RFC 9150 suites default: off * in the default cipher suite list (requires * HAVE_NULL_CIPHER). Without it the integrity-only * suites must be requested explicitly in the * cipher list, by name or with "eNULL". * * TLS 1.3 PSK: * WOLFSSL_PSK_ONE_ID: Single PSK identity per connect default: off * WOLFSSL_PSK_MULTI_ID_PER_CS: Multiple PSK IDs per cipher suite default: off * WOLFSSL_PRIORITIZE_PSK: Prioritize PSK over ciphersuite order default: off * * TLS 1.3 Session Tickets: * WOLFSSL_TICKET_HAVE_ID: Session tickets include ID default: off * Forced on when WOLFSSL_EARLY_DATA is set. * WOLFSSL_TICKET_NONCE_MALLOC: Dynamically allocate ticket nonce default: off * WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES: Withhold NewSessionTicket default: off * when the ClientHello advertised no usable * psk_key_exchange_modes, as RFC 9846 Sections * 4.3.9 and 4.7.1 require. Off by default: a peer * that omits the extension but expects a ticket * stops getting one. * * TLS 1.3 Key Exchange: * HAVE_KEYING_MATERIAL: Export keying material (RFC 8446 7.5) default: off * WOLFSSL_HAVE_TLS_UNIQUE: Enable tls-unique channel binding default: off * * TLS 1.3 Hash/Signature: * WOLFSSL_TLS13_SHA512: Allow SHA-512 in TLS 1.3 handshake default: off * (no ciphersuite requires it currently) * WOLFSSL_ERROR_CODE_OPENSSL: Use OpenSSL-compatible error codes default: off * WOLFSSL_SSLKEYLOGFILE_OUTPUT: Set key log output file path default: off * WOLFSSL_SSLKEYLOGFILE_USE_ENV: Use SSLKEYLOGFILE env var path default: off * WOLFSSL_RW_THREADED: Enable read/write threading support default: off * WOLFSSL_ASYNC_IO: Enable async I/O operations default: off * WOLFSSL_NONBLOCK_OCSP: Non-blocking OCSP processing default: off * WOLFSSL_TLS_OCSP_MULTI: Multiple OCSP responses default: off * WOLFSSL_WOLFSENTRY_HOOKS: wolfSentry integration hooks default: off */ #if !defined(NO_TLS) && defined(WOLFSSL_TLS13) /* 0-RTT anti-replay eviction needs the session cache. */ #if defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET) && \ defined(NO_SESSION_CACHE) && !defined(NO_WOLFSSL_SERVER) && \ !defined(WOLFSSL_EARLY_DATA_NO_ANTI_REPLAY) #error "WOLFSSL_EARLY_DATA with tickets requires !NO_SESSION_CACHE, or " \ "define WOLFSSL_EARLY_DATA_NO_ANTI_REPLAY to opt out." #endif #ifndef WOLFCRYPT_ONLY #ifdef HAVE_ERRNO_H #include #endif #if defined(__MACH__) || defined(__FreeBSD__) || \ defined(__INCLUDE_NUTTX_CONFIG_H) || defined(WOLFSSL_RIOT_OS) #include #endif /* __MACH__ || __FreeBSD__ || __INCLUDE_NUTTX_CONFIG_H || WOLFSSL_RIOT_OS */ #include #include #include #include #include #include #ifdef NO_INLINE #include #else #define WOLFSSL_MISC_INCLUDED #include #endif #ifdef __sun #include #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef HAVE_AEAD #if !defined(_MSC_VER) && !defined(__TASKING__) #error "The build option HAVE_AEAD is required for TLS 1.3" #else #pragma \ message("error: The build option HAVE_AEAD is required for TLS 1.3") #endif #endif #ifndef HAVE_HKDF #if !defined(_MSC_VER) && !defined(__TASKING__) #error "The build option HAVE_HKDF is required for TLS 1.3" #else #pragma message("error: The build option HAVE_HKDF is required for TLS 1.3") #endif #endif #ifndef HAVE_TLS_EXTENSIONS #if !defined(_MSC_VER) && !defined(__TASKING__) #error "The build option HAVE_TLS_EXTENSIONS is required for TLS 1.3" #else #pragma message("error: The build option HAVE_TLS_EXTENSIONS is required for TLS 1.3") #endif #endif /* Set ret to error value and jump to label. * * err The error value to set. * eLabel The label to jump to. */ #define ERROR_OUT(err, eLabel) { ret = (err); goto eLabel; } /* Senders suspend/resume a pending record build only on the re-invoke path; * poll-completing backends block inside EncryptTls13() as before. */ #if defined(WOLFSSL_ASYNC_REINVOKE) && !defined(WOLF_CRYPTO_CB_ASYNC_POLL) #define TLS13_HS_ASYNC_OKAY 1 #else #define TLS13_HS_ASYNC_OKAY 0 #endif #ifdef WOLFSSL_ASYNC_REINVOKE /* Arm ssl->kdfAsyncDev for a key-schedule op that may pend, retiring this * connection's previous KDF event first (re-pushing a queued event would * self-link the event list). Returns 0 on success. */ static int Tls13KdfAsyncInit(WOLFSSL* ssl) { int ret; if (ssl->asyncDev == &ssl->kdfAsyncDev) { ret = wolfSSL_AsyncPop(ssl, NULL); if (ret != 0 && ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E) && ret != WC_NO_ERR_TRACE(WC_PENDING_E)) { return ret; } } #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_ASYNC_POLL) /* Never poll-routed: a zeroed cryptocbDevId would look poll-stamped to * wolfSSL_AsyncPop() and stop the resume state advancing. */ ssl->kdfAsyncDev.cryptocbDevId = INVALID_DEVID; #endif return wolfSSL_AsyncInit(ssl, &ssl->kdfAsyncDev, WC_ASYNC_FLAG_CALL_AGAIN); } #endif /* WOLFSSL_ASYNC_REINVOKE */ /* Cap on re-invoking a callback for a record the caller cannot resume * (alerts, asyncOkay = 0 senders); bounds the otherwise unbounded spin. */ #ifndef WOLFSSL_ASYNC_MAX_REINVOKE #define WOLFSSL_ASYNC_MAX_REINVOKE 1000 #endif /* Size of the TLS v1.3 label use when deriving keys. */ #define TLS13_PROTOCOL_LABEL_SZ 6 /* The protocol label for TLS v1.3. */ static const byte tls13ProtocolLabel[TLS13_PROTOCOL_LABEL_SZ + 1] = "tls13 "; #ifdef WOLFSSL_DTLS13 #define DTLS13_PROTOCOL_LABEL_SZ 6 static const byte dtls13ProtocolLabel[DTLS13_PROTOCOL_LABEL_SZ + 1] = "dtls13"; #endif /* WOLFSSL_DTLS13 */ #if defined(HAVE_ECH) #define ECH_ACCEPT_CONFIRMATION_LABEL_SZ 23 #define ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ 27 static const byte echAcceptConfirmationLabel[ECH_ACCEPT_CONFIRMATION_LABEL_SZ + 1] = "ech accept confirmation"; static const byte echHrrAcceptConfirmationLabel[ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ + 1] = "hrr ech accept confirmation"; #endif #ifndef NO_CERTS #if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_ED448) || defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ defined(WOLFSSL_HAVE_SLHDSA) static WC_INLINE int GetMsgHash(WOLFSSL* ssl, byte* hash); #endif #endif /* Expand data using HMAC, salt and label and info. * TLS v1.3 defines this function. Use callback if available. */ static int Tls13HKDFExpandLabel(WOLFSSL* ssl, byte* okm, word32 okmLen, const byte* prk, word32 prkLen, const byte* protocol, word32 protocolLen, const byte* label, word32 labelLen, const byte* info, word32 infoLen, int digest) { int ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); #ifdef WOLFSSL_ASYNC_REINVOKE int aret; /* Armed before the provider runs so a pending from the PK callback or * wolfCrypt path falls through to the single push below. */ aret = Tls13KdfAsyncInit(ssl); if (aret != 0) return aret; #endif #if defined(HAVE_PK_CALLBACKS) if (ssl->ctx && ssl->ctx->HKDFExpandLabelCb) { ret = ssl->ctx->HKDFExpandLabelCb(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, info, infoLen, digest, WOLFSSL_CLIENT_END /* ignored */); } if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) #endif { PRIVATE_KEY_UNLOCK(); #if !defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)) ret = wc_Tls13_HKDF_Expand_Label_ex(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, info, infoLen, digest, ssl->heap, ssl->devId); #else (void)ssl; ret = wc_Tls13_HKDF_Expand_Label(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, info, infoLen, digest); #endif PRIVATE_KEY_LOCK(); } #ifdef WOLFSSL_ASYNC_REINVOKE /* HKDF has no key object to carry a WC_ASYNC_DEV, so queue the * SSL-owned KDF device; without it the pop finds nothing pending and * replays the handshake message. CALL_AGAIN keeps the state put. */ if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) ret = wolfSSL_AsyncPush(ssl, &ssl->kdfAsyncDev); #endif return ret; } /* Same as above, but pass in the side we are expanding for: * side: either WOLFSSL_CLIENT_END or WOLFSSL_SERVER_END. */ static int Tls13HKDFExpandKeyLabel(WOLFSSL* ssl, byte* okm, word32 okmLen, const byte* prk, word32 prkLen, const byte* protocol, word32 protocolLen, const byte* label, word32 labelLen, const byte* info, word32 infoLen, int digest, int side) { int ret; #ifdef WOLFSSL_ASYNC_REINVOKE ret = Tls13KdfAsyncInit(ssl); if (ret != 0) return ret; #endif #if defined(HAVE_PK_CALLBACKS) ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); if (ssl->ctx && ssl->ctx->HKDFExpandLabelCb) { ret = ssl->ctx->HKDFExpandLabelCb(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, info, infoLen, digest, side); } /* No early return: a pending here must reach the push at the end. */ if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) #endif { #if !defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)) ret = wc_Tls13_HKDF_Expand_Label_ex(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, info, infoLen, digest, ssl->heap, ssl->devId); #elif defined(HAVE_FIPS) && defined(wc_Tls13_HKDF_Expand_Label) ret = wc_Tls13_HKDF_Expand_Label_fips(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, info, infoLen, digest); #else ret = wc_Tls13_HKDF_Expand_Label(okm, okmLen, prk, prkLen, protocol, protocolLen, label, labelLen, info, infoLen, digest); #endif } #ifdef WOLFSSL_ASYNC_REINVOKE /* HKDF has no key object to carry a WC_ASYNC_DEV, so queue the * SSL-owned KDF device; without it the pop finds nothing pending and * replays the handshake message. CALL_AGAIN keeps the state put. */ if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) ret = wolfSSL_AsyncPush(ssl, &ssl->kdfAsyncDev); #endif (void)ssl; (void)side; return ret; } /* Derive a key from a message. * * ssl The SSL/TLS object. * output The buffer to hold the derived key. * outputLen The length of the derived key. * secret The secret used to derive the key (HMAC secret). * label The label used to distinguish the context. * labelLen The length of the label. * msg The message data to derive key from. * msgLen The length of the message data to derive key from. * hashAlgo The hash algorithm to use in the HMAC. * returns 0 on success, otherwise failure. */ static int DeriveKeyMsg(WOLFSSL* ssl, byte* output, int outputLen, const byte* secret, const byte* label, word32 labelLen, byte* msg, int msgLen, int hashAlgo) { byte hash[WC_MAX_DIGEST_SIZE]; Digest digest; word32 hashSz = 0; const byte* protocol; word32 protocolLen; int digestAlg = -1; int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); switch (hashAlgo) { #ifndef NO_SHA256 case sha256_mac: ret = wc_InitSha256_ex(&digest.sha256, ssl->heap, ssl->devId); if (ret == 0) { ret = wc_Sha256Update(&digest.sha256, msg, (word32)msgLen); if (ret == 0) ret = wc_Sha256Final(&digest.sha256, hash); wc_Sha256Free(&digest.sha256); } hashSz = WC_SHA256_DIGEST_SIZE; digestAlg = WC_SHA256; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: ret = wc_InitSha384_ex(&digest.sha384, ssl->heap, ssl->devId); if (ret == 0) { ret = wc_Sha384Update(&digest.sha384, msg, (word32)msgLen); if (ret == 0) ret = wc_Sha384Final(&digest.sha384, hash); wc_Sha384Free(&digest.sha384); } hashSz = WC_SHA384_DIGEST_SIZE; digestAlg = WC_SHA384; break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: ret = wc_InitSha512_ex(&digest.sha512, ssl->heap, ssl->devId); if (ret == 0) { ret = wc_Sha512Update(&digest.sha512, msg, (word32)msgLen); if (ret == 0) ret = wc_Sha512Final(&digest.sha512, hash); wc_Sha512Free(&digest.sha512); } hashSz = WC_SHA512_DIGEST_SIZE; digestAlg = WC_SHA512; break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: ret = wc_InitSm3(&digest.sm3, ssl->heap, ssl->devId); if (ret == 0) { ret = wc_Sm3Update(&digest.sm3, msg, (word32)msgLen); if (ret == 0) ret = wc_Sm3Final(&digest.sm3, hash); wc_Sm3Free(&digest.sm3); } hashSz = WC_SM3_DIGEST_SIZE; digestAlg = WC_SM3; break; #endif default: ret = BAD_FUNC_ARG; digestAlg = -1; break; } if (digestAlg < 0) return HASH_TYPE_E; if (ret != 0) return ret; switch (ssl->version.minor) { case TLSv1_3_MINOR: protocol = tls13ProtocolLabel; protocolLen = TLS13_PROTOCOL_LABEL_SZ; break; #ifdef WOLFSSL_DTLS13 case DTLSv1_3_MINOR: if (!ssl->options.dtls) return VERSION_ERROR; protocol = dtls13ProtocolLabel; protocolLen = DTLS13_PROTOCOL_LABEL_SZ; break; #endif /* WOLFSSL_DTLS13 */ default: return VERSION_ERROR; } if (outputLen == -1) outputLen = (int)hashSz; ret = Tls13HKDFExpandLabel(ssl, output, (word32)outputLen, secret, hashSz, protocol, protocolLen, label, labelLen, hash, hashSz, digestAlg); return ret; } /* Derive a key. * * ssl The SSL/TLS object. * output The buffer to hold the derived key. * outputLen The length of the derived key. * secret The secret used to derive the key (HMAC secret). * label The label used to distinguish the context. * labelLen The length of the label. * hashAlgo The hash algorithm to use in the HMAC. * includeMsgs Whether to include a hash of the handshake messages so far. * side The side that we are deriving the secret for. * returns 0 on success, otherwise failure. */ int Tls13DeriveKey(WOLFSSL* ssl, byte* output, int outputLen, const byte* secret, const byte* label, word32 labelLen, int hashAlgo, int includeMsgs, int side) { int ret = 0; byte hash[WC_MAX_DIGEST_SIZE]; word32 hashSz = 0; word32 hashOutSz = 0; const byte* protocol; word32 protocolLen; int digestAlg = 0; switch (hashAlgo) { #ifndef NO_SHA256 case sha256_mac: hashSz = WC_SHA256_DIGEST_SIZE; digestAlg = WC_SHA256; if (includeMsgs) ret = wc_Sha256GetHash(&ssl->hsHashes->hashSha256, hash); break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: hashSz = WC_SHA384_DIGEST_SIZE; digestAlg = WC_SHA384; if (includeMsgs) ret = wc_Sha384GetHash(&ssl->hsHashes->hashSha384, hash); break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: hashSz = WC_SHA512_DIGEST_SIZE; digestAlg = WC_SHA512; if (includeMsgs) ret = wc_Sha512GetHash(&ssl->hsHashes->hashSha512, hash); break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: hashSz = WC_SM3_DIGEST_SIZE; digestAlg = WC_SM3; if (includeMsgs) ret = wc_Sm3GetHash(&ssl->hsHashes->hashSm3, hash); break; #endif default: ret = HASH_TYPE_E; break; } if (ret != 0) return ret; protocol = tls13ProtocolLabel; protocolLen = TLS13_PROTOCOL_LABEL_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { protocol = dtls13ProtocolLabel; protocolLen = DTLS13_PROTOCOL_LABEL_SZ; } #endif /* WOLFSSL_DTLS13 */ if (outputLen == -1) { outputLen = (int)hashSz; } if (includeMsgs) { hashOutSz = hashSz; } else { /* Appease static analyzers by making sure hash is cleared, since it is * passed into expand key label where older wc_Tls13_HKDF_Expand_Label * will unconditionally try to call a memcpy on it, however length will * always be 0. */ XMEMSET(hash, 0, sizeof(hash)); hashOutSz = 0; } PRIVATE_KEY_UNLOCK(); ret = Tls13HKDFExpandKeyLabel(ssl, output, (word32)outputLen, secret, hashSz, protocol, protocolLen, label, labelLen, hash, hashOutSz, digestAlg, side); PRIVATE_KEY_LOCK(); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("TLS 1.3 derived key", output, outputLen); #endif return ret; } /* Convert TLS mac ID to a hash algorithm ID * * mac Mac ID to convert * returns hash ID on success, or the NONE type. */ static WC_INLINE int mac2hash(int mac) { int hash; switch (mac) { #ifndef NO_SHA256 case sha256_mac: hash = WC_SHA256; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: hash = WC_SHA384; break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: hash = WC_SHA512; break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: hash = WC_SM3; break; #endif default: hash = WC_HASH_TYPE_NONE; } return hash; } #ifndef NO_PSK /* The length of the binder key label. */ #define BINDER_KEY_LABEL_SZ 10 /* The binder key label. */ static const byte binderKeyLabel[BINDER_KEY_LABEL_SZ + 1] = "ext binder"; /* Derive the binder key. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ static int DeriveBinderKey(WOLFSSL* ssl, byte* key) { WOLFSSL_MSG("Derive Binder Key"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } return DeriveKeyMsg(ssl, key, -1, ssl->arrays->secret, binderKeyLabel, BINDER_KEY_LABEL_SZ, NULL, 0, ssl->specs.mac_algorithm); } #endif /* !NO_PSK */ #if defined(HAVE_SESSION_TICKET) && \ (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) /* The length of the binder key resume label. */ #define BINDER_KEY_RESUME_LABEL_SZ 10 /* The binder key resume label. */ static const byte binderKeyResumeLabel[BINDER_KEY_RESUME_LABEL_SZ + 1] = "res binder"; /* Derive the binder resumption key. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ static int DeriveBinderKeyResume(WOLFSSL* ssl, byte* key) { WOLFSSL_MSG("Derive Binder Key - Resumption"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } return DeriveKeyMsg(ssl, key, -1, ssl->arrays->secret, binderKeyResumeLabel, BINDER_KEY_RESUME_LABEL_SZ, NULL, 0, ssl->specs.mac_algorithm); } #endif /* HAVE_SESSION_TICKET && (!NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER) */ #ifdef WOLFSSL_EARLY_DATA /* The length of the early traffic label. */ #define EARLY_TRAFFIC_LABEL_SZ 11 /* The early traffic label. */ static const byte earlyTrafficLabel[EARLY_TRAFFIC_LABEL_SZ + 1] = "c e traffic"; /* Derive the early traffic key. * * ssl The SSL/TLS object. * key The derived key. * side The side that we are deriving the secret for. * returns 0 on success, otherwise failure. */ static int DeriveEarlyTrafficSecret(WOLFSSL* ssl, byte* key, int side) { int ret; WOLFSSL_MSG("Derive Early Traffic Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE) /* If this is called from a sniffer session with keylog file support, * obtain the appropriate secret from the callback */ if (ssl->snifferSecretCb != NULL) { return ssl->snifferSecretCb(ssl->arrays->clientRandom, SNIFFER_SECRET_CLIENT_EARLY_TRAFFIC_SECRET, key); } #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->secret, earlyTrafficLabel, EARLY_TRAFFIC_LABEL_SZ, ssl->specs.mac_algorithm, 1, side); #ifdef HAVE_SECRET_CALLBACK if (ret == 0 && ssl->tls13SecretCb != NULL) { ret = ssl->tls13SecretCb(ssl, CLIENT_EARLY_TRAFFIC_SECRET, key, ssl->specs.hash_size, ssl->tls13SecretCtx); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #ifdef OPENSSL_EXTRA if (ret == 0 && ssl->tls13KeyLogCb != NULL) { ret = ssl->tls13KeyLogCb(ssl, CLIENT_EARLY_TRAFFIC_SECRET, key, ssl->specs.hash_size, NULL); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #endif /* OPENSSL_EXTRA */ #endif /* HAVE_SECRET_CALLBACK */ return ret; } #endif /* The length of the client handshake label. */ #define CLIENT_HANDSHAKE_LABEL_SZ 12 /* The client handshake label. */ static const byte clientHandshakeLabel[CLIENT_HANDSHAKE_LABEL_SZ + 1] = "c hs traffic"; /* Derive the client handshake key. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ static int DeriveClientHandshakeSecret(WOLFSSL* ssl, byte* key) { int ret; WOLFSSL_MSG("Derive Client Handshake Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE) /* If this is called from a sniffer session with keylog file support, * obtain the appropriate secret from the callback */ if (ssl->snifferSecretCb != NULL) { return ssl->snifferSecretCb(ssl->arrays->clientRandom, SNIFFER_SECRET_CLIENT_HANDSHAKE_TRAFFIC_SECRET, key); } #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->preMasterSecret, clientHandshakeLabel, CLIENT_HANDSHAKE_LABEL_SZ, ssl->specs.mac_algorithm, 1, WOLFSSL_CLIENT_END); #ifdef HAVE_SECRET_CALLBACK if (ret == 0 && ssl->tls13SecretCb != NULL) { ret = ssl->tls13SecretCb(ssl, CLIENT_HANDSHAKE_TRAFFIC_SECRET, key, ssl->specs.hash_size, ssl->tls13SecretCtx); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #ifdef OPENSSL_EXTRA if (ret == 0 && ssl->tls13KeyLogCb != NULL) { ret = ssl->tls13KeyLogCb(ssl, CLIENT_HANDSHAKE_TRAFFIC_SECRET, key, ssl->specs.hash_size, NULL); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #endif /* OPENSSL_EXTRA */ #endif /* HAVE_SECRET_CALLBACK */ return ret; } /* The length of the server handshake label. */ #define SERVER_HANDSHAKE_LABEL_SZ 12 /* The server handshake label. */ static const byte serverHandshakeLabel[SERVER_HANDSHAKE_LABEL_SZ + 1] = "s hs traffic"; /* Derive the server handshake key. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ static int DeriveServerHandshakeSecret(WOLFSSL* ssl, byte* key) { int ret; WOLFSSL_MSG("Derive Server Handshake Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE) /* If this is called from a sniffer session with keylog file support, * obtain the appropriate secret from the callback */ if (ssl->snifferSecretCb != NULL) { return ssl->snifferSecretCb(ssl->arrays->clientRandom, SNIFFER_SECRET_SERVER_HANDSHAKE_TRAFFIC_SECRET, key); } #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->preMasterSecret, serverHandshakeLabel, SERVER_HANDSHAKE_LABEL_SZ, ssl->specs.mac_algorithm, 1, WOLFSSL_SERVER_END); #ifdef HAVE_SECRET_CALLBACK if (ret == 0 && ssl->tls13SecretCb != NULL) { ret = ssl->tls13SecretCb(ssl, SERVER_HANDSHAKE_TRAFFIC_SECRET, key, ssl->specs.hash_size, ssl->tls13SecretCtx); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #ifdef OPENSSL_EXTRA if (ret == 0 && ssl->tls13KeyLogCb != NULL) { ret = ssl->tls13KeyLogCb(ssl, SERVER_HANDSHAKE_TRAFFIC_SECRET, key, ssl->specs.hash_size, NULL); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #endif /* OPENSSL_EXTRA */ #endif /* HAVE_SECRET_CALLBACK */ return ret; } /* The length of the client application traffic label. */ #define CLIENT_APP_LABEL_SZ 12 /* The client application traffic label. */ static const byte clientAppLabel[CLIENT_APP_LABEL_SZ + 1] = "c ap traffic"; /* Derive the client application traffic key. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ static int DeriveClientTrafficSecret(WOLFSSL* ssl, byte* key) { int ret; WOLFSSL_MSG("Derive Client Traffic Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE) /* If this is called from a sniffer session with keylog file support, * obtain the appropriate secret from the callback */ if (ssl->snifferSecretCb != NULL) { return ssl->snifferSecretCb(ssl->arrays->clientRandom, SNIFFER_SECRET_CLIENT_TRAFFIC_SECRET, key); } #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->masterSecret, clientAppLabel, CLIENT_APP_LABEL_SZ, ssl->specs.mac_algorithm, 1, WOLFSSL_CLIENT_END); #ifdef HAVE_SECRET_CALLBACK if (ret == 0 && ssl->tls13SecretCb != NULL) { ret = ssl->tls13SecretCb(ssl, CLIENT_TRAFFIC_SECRET, key, ssl->specs.hash_size, ssl->tls13SecretCtx); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #ifdef OPENSSL_EXTRA if (ret == 0 && ssl->tls13KeyLogCb != NULL) { ret = ssl->tls13KeyLogCb(ssl, CLIENT_TRAFFIC_SECRET, key, ssl->specs.hash_size, NULL); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #endif /* OPENSSL_EXTRA */ #endif /* HAVE_SECRET_CALLBACK */ return ret; } /* The length of the server application traffic label. */ #define SERVER_APP_LABEL_SZ 12 /* The server application traffic label. */ static const byte serverAppLabel[SERVER_APP_LABEL_SZ + 1] = "s ap traffic"; /* Derive the server application traffic key. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ static int DeriveServerTrafficSecret(WOLFSSL* ssl, byte* key) { int ret; WOLFSSL_MSG("Derive Server Traffic Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE) /* If this is called from a sniffer session with keylog file support, * obtain the appropriate secret from the callback */ if (ssl->snifferSecretCb != NULL) { return ssl->snifferSecretCb(ssl->arrays->clientRandom, SNIFFER_SECRET_SERVER_TRAFFIC_SECRET, key); } #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->masterSecret, serverAppLabel, SERVER_APP_LABEL_SZ, ssl->specs.mac_algorithm, 1, WOLFSSL_SERVER_END); #ifdef HAVE_SECRET_CALLBACK if (ret == 0 && ssl->tls13SecretCb != NULL) { ret = ssl->tls13SecretCb(ssl, SERVER_TRAFFIC_SECRET, key, ssl->specs.hash_size, ssl->tls13SecretCtx); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #ifdef OPENSSL_EXTRA if (ret == 0 && ssl->tls13KeyLogCb != NULL) { ret = ssl->tls13KeyLogCb(ssl, SERVER_TRAFFIC_SECRET, key, ssl->specs.hash_size, NULL); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #endif /* OPENSSL_EXTRA */ #endif /* HAVE_SECRET_CALLBACK */ return ret; } #ifdef HAVE_KEYING_MATERIAL /* The length of the exporter master secret label. */ #define EXPORTER_MASTER_LABEL_SZ 10 /* The exporter master secret label. */ static const byte exporterMasterLabel[EXPORTER_MASTER_LABEL_SZ + 1] = "exp master"; /* Derive the exporter secret. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ static int DeriveExporterSecret(WOLFSSL* ssl, byte* key) { int ret; WOLFSSL_ENTER("Derive Exporter Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->masterSecret, exporterMasterLabel, EXPORTER_MASTER_LABEL_SZ, ssl->specs.mac_algorithm, 1, 0 /* Unused */); #ifdef HAVE_SECRET_CALLBACK if (ret == 0 && ssl->tls13SecretCb != NULL) { ret = ssl->tls13SecretCb(ssl, EXPORTER_SECRET, key, ssl->specs.hash_size, ssl->tls13SecretCtx); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #ifdef OPENSSL_EXTRA if (ret == 0 && ssl->tls13KeyLogCb != NULL) { ret = ssl->tls13KeyLogCb(ssl, EXPORTER_SECRET, key, ssl->specs.hash_size, NULL); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); return TLS13_SECRET_CB_E; } } #endif /* OPENSSL_EXTRA */ #endif /* HAVE_SECRET_CALLBACK */ return ret; } /* The length of the exporter label. */ #define EXPORTER_LABEL_SZ 8 /* The exporter label. */ static const byte exporterLabel[EXPORTER_LABEL_SZ + 1] = "exporter"; /* Hash("") */ #ifndef NO_SHA256 static const byte emptySHA256Hash[] = { 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55 }; #endif #ifdef WOLFSSL_SHA384 static const byte emptySHA384Hash[] = { 0x38, 0xB0, 0x60, 0xA7, 0x51, 0xAC, 0x96, 0x38, 0x4C, 0xD9, 0x32, 0x7E, 0xB1, 0xB1, 0xE3, 0x6A, 0x21, 0xFD, 0xB7, 0x11, 0x14, 0xBE, 0x07, 0x43, 0x4C, 0x0C, 0xC7, 0xBF, 0x63, 0xF6, 0xE1, 0xDA, 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B }; #endif #ifdef WOLFSSL_TLS13_SHA512 static const byte emptySHA512Hash[] = { 0xCF, 0x83, 0xE1, 0x35, 0x7E, 0xEF, 0xB8, 0xBD, 0xF1, 0x54, 0x28, 0x50, 0xD6, 0x6D, 0x80, 0x07, 0xD6, 0x20, 0xE4, 0x05, 0x0B, 0x57, 0x15, 0xDC, 0x83, 0xF4, 0xA9, 0x21, 0xD3, 0x6C, 0xE9, 0xCE, 0x47, 0xD0, 0xD1, 0x3C, 0x5D, 0x85, 0xF2, 0xB0, 0xFF, 0x83, 0x18, 0xD2, 0x87, 0x7E, 0xEC, 0x2F, 0x63, 0xB9, 0x31, 0xBD, 0x47, 0x41, 0x7A, 0x81, 0xA5, 0x38, 0x32, 0x7A, 0xF9, 0x27, 0xDA, 0x3E }; #endif #ifdef WOLFSSL_SM3 static const byte emptySM3Hash[] = { 0x1A, 0xB2, 0x1D, 0x83, 0x55, 0xCF, 0xA1, 0x7F, 0x8E, 0x61, 0x19, 0x48, 0x31, 0xE8, 0x1A, 0x8F, 0x22, 0xBE, 0xC8, 0xC7, 0x28, 0xFE, 0xFB, 0x74, 0x7E, 0xD0, 0x35, 0xEB, 0x50, 0x82, 0xAA, 0x2B }; #endif /** * Implement section 7.5 of RFC 8446 * @return 0 on success * <0 on failure */ int Tls13_Exporter(WOLFSSL* ssl, unsigned char *out, size_t outLen, const char *label, size_t labelLen, const unsigned char *context, size_t contextLen) { int ret; enum wc_HashType hashType = WC_HASH_TYPE_NONE; word32 hashLen = 0; byte hashOut[WC_MAX_DIGEST_SIZE]; const byte* emptyHash = NULL; byte firstExpand[WC_MAX_DIGEST_SIZE]; const byte* protocol = tls13ProtocolLabel; word32 protocolLen = TLS13_PROTOCOL_LABEL_SZ; if (ssl->options.dtls && ssl->version.minor != DTLSv1_3_MINOR) return VERSION_ERROR; if (!ssl->options.dtls && ssl->version.minor != TLSv1_3_MINOR) return VERSION_ERROR; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { protocol = dtls13ProtocolLabel; protocolLen = DTLS13_PROTOCOL_LABEL_SZ; } #endif /* WOLFSSL_DTLS13 */ /* Sanity check contextLen to prevent truncation when cast to word32. */ if (contextLen > WOLFSSL_MAX_32BIT) return BAD_FUNC_ARG; /* RFC 8446 HkdfLabel encodes the output length as a uint16, so requested * lengths > 65535 cannot be represented and must be rejected. */ if (outLen > WOLFSSL_MAX_16BIT) return BAD_FUNC_ARG; /* RFC 8446 HkdfLabel encodes the label length in a single byte, so * anything > 255 cannot be represented and must be rejected. * The protocol length is included in the label. */ if ((labelLen + protocolLen) > WOLFSSL_MAX_8BIT) return BAD_FUNC_ARG; switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: hashType = WC_HASH_TYPE_SHA256; hashLen = WC_SHA256_DIGEST_SIZE; emptyHash = emptySHA256Hash; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: hashType = WC_HASH_TYPE_SHA384; hashLen = WC_SHA384_DIGEST_SIZE; emptyHash = emptySHA384Hash; break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: hashType = WC_HASH_TYPE_SHA512; hashLen = WC_SHA512_DIGEST_SIZE; emptyHash = emptySHA512Hash; break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: hashType = WC_HASH_TYPE_SM3; hashLen = WC_SM3_DIGEST_SIZE; emptyHash = emptySM3Hash; break; #endif default: return BAD_FUNC_ARG; } #ifdef WOLFSSL_CHECK_MEM_ZERO /* Poison and register firstExpand before it is written so that any path * below (all of which funnel through cleanup) is covered. */ XMEMSET(firstExpand, 0xff, sizeof(firstExpand)); wc_MemZero_Add("Tls13_Exporter firstExpand", firstExpand, sizeof(firstExpand)); #endif /* Derive-Secret(Secret, label, "") */ ret = Tls13HKDFExpandLabel(ssl, firstExpand, hashLen, ssl->arrays->exporterSecret, hashLen, protocol, protocolLen, (byte*)label, (word32)labelLen, emptyHash, hashLen, (int)hashType); if (ret != 0) goto cleanup; /* Hash(context_value) */ ret = wc_Hash(hashType, context, (word32)contextLen, hashOut, WC_MAX_DIGEST_SIZE); if (ret != 0) goto cleanup; ret = Tls13HKDFExpandLabel(ssl, out, (word32)outLen, firstExpand, hashLen, protocol, protocolLen, exporterLabel, EXPORTER_LABEL_SZ, hashOut, hashLen, (int)hashType); cleanup: /* firstExpand is the per-label Derive-Secret PRK and hashOut holds * Hash(context_value); wipe both before the stack frame is reclaimed. */ ForceZero(firstExpand, sizeof(firstExpand)); ForceZero(hashOut, sizeof(hashOut)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(firstExpand, sizeof(firstExpand)); #endif return ret; } #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* The length of the resumption master secret label. */ #define RESUME_MASTER_LABEL_SZ 10 /* The resumption master secret label. */ static const byte resumeMasterLabel[RESUME_MASTER_LABEL_SZ + 1] = "res master"; /* Derive the resumption secret. * * ssl The SSL/TLS object. * key The derived key. * returns 0 on success, otherwise failure. */ int DeriveResumptionSecret(WOLFSSL* ssl, byte* key) { byte* masterSecret; WOLFSSL_MSG("Derive Resumption Secret"); if (ssl == NULL) { return BAD_FUNC_ARG; } if (ssl->arrays != NULL) { masterSecret = ssl->arrays->masterSecret; } else { masterSecret = ssl->session->masterSecret; } return Tls13DeriveKey(ssl, key, -1, masterSecret, resumeMasterLabel, RESUME_MASTER_LABEL_SZ, ssl->specs.mac_algorithm, 1, 0 /* Unused */); } #endif #ifdef WOLFSSL_SESSION_EXPORT /* Serialize the TLS 1.3 state the record layer sections of a session export * leave out, for a stream and a DTLS session alike: the traffic secrets the * next KeyUpdate derives its keys from, the resumption secret the tickets * issued or received from here on derive their PSK from, a KeyUpdate response * still due from the peer and the nonce of the last ticket issued. * Returns the number of bytes written to 'exp' or a negative value. */ int ExportTls13State(WOLFSSL* ssl, byte* exp, word32 len) { word32 idx = 0; byte secretSz; byte nonceLen = 0; WOLFSSL_ENTER("ExportTls13State"); if (ssl == NULL || exp == NULL) return BAD_FUNC_ARG; /* the secrets are the traffic ones only once the handshake is done */ if (!ssl->options.handShakeDone) { WOLFSSL_MSG("Can not export before the handshake is done"); return BAD_STATE_E; } /* a KeyUpdate response the read side left for the write side to send */ if (ssl->options.sendKeyUpdate) { WOLFSSL_MSG("Can not export with a KeyUpdate response pending"); return BAD_STATE_E; } secretSz = ssl->specs.hash_size; if (secretSz > SECRET_LEN) return BAD_STATE_E; /* The nonce of the last ticket issued, the per-connection count that * SendTls13NewSessionTicket() steps for every ticket and that has to stay * unique on the connection (RFC 8446 Section 4.6.1); unset before the * first ticket. A client holds the nonce of the ticket it received, which * belongs to the session, not to the connection, so it is not exported. */ #ifdef HAVE_SESSION_TICKET if (ssl->options.side == WOLFSSL_SERVER_END) nonceLen = ssl->session->ticketNonce.len; if (nonceLen > DEF_TICKET_NONCE_SZ) return BAD_STATE_E; #endif /* secret length, the three secrets, the KeyUpdate response flag, the nonce * length and a fixed DEF_TICKET_NONCE_SZ wide nonce field */ if (OPAQUE8_LEN + (3u * secretSz) + (2 * OPAQUE8_LEN) + DEF_TICKET_NONCE_SZ > len) return BUFFER_E; exp[idx++] = secretSz; XMEMCPY(exp + idx, ssl->clientSecret, secretSz); idx += secretSz; XMEMCPY(exp + idx, ssl->serverSecret, secretSz); idx += secretSz; XMEMCPY(exp + idx, ssl->session->masterSecret, secretSz); idx += secretSz; /* a KeyUpdate that asked for a response the peer has not sent yet */ exp[idx++] = ssl->keys.updateResponseReq; /* the nonce length, then the nonce itself in a DEF_TICKET_NONCE_SZ wide * field so the section size does not depend on whether a ticket was sent */ exp[idx++] = nonceLen; XMEMSET(exp + idx, 0, DEF_TICKET_NONCE_SZ); #ifdef HAVE_SESSION_TICKET if (nonceLen > 0) XMEMCPY(exp + idx, ssl->session->ticketNonce.data, nonceLen); #endif idx += DEF_TICKET_NONCE_SZ; WOLFSSL_LEAVE("ExportTls13State", (int)idx); return (int)idx; } /* Parse what ExportTls13State() wrote into 'ssl', whose cipher specs and * options were imported ahead of it. * Returns the number of bytes read from 'exp' or a negative value. */ int ImportTls13State(WOLFSSL* ssl, const byte* exp, word32 len) { word32 idx = 0; byte secretSz; byte nonceLen; WOLFSSL_ENTER("ImportTls13State"); if (ssl == NULL || exp == NULL) return BAD_FUNC_ARG; if (OPAQUE8_LEN > len) return BUFFER_E; secretSz = exp[idx++]; /* every key is derived over specs.hash_size bytes of the secrets; the * three secrets are followed by the KeyUpdate response flag, the nonce * length and a DEF_TICKET_NONCE_SZ wide nonce field */ if (secretSz != ssl->specs.hash_size || secretSz > SECRET_LEN || idx + (3u * secretSz) + (2 * OPAQUE8_LEN) + DEF_TICKET_NONCE_SZ > len) return BUFFER_E; XMEMCPY(ssl->clientSecret, exp + idx, secretSz); idx += secretSz; XMEMCPY(ssl->serverSecret, exp + idx, secretSz); idx += secretSz; XMEMCPY(ssl->session->masterSecret, exp + idx, secretSz); idx += secretSz; /* the KeyUpdate response still due from the peer */ if (exp[idx] > 1) return BUFFER_E; ssl->keys.updateResponseReq = exp[idx++]; /* the nonce the next ticket is numbered after, only meaningful on the * server; a client blob carries a zero length here */ nonceLen = exp[idx++]; if (nonceLen > DEF_TICKET_NONCE_SZ) return BUFFER_E; #ifdef HAVE_SESSION_TICKET if (ssl->options.side == WOLFSSL_SERVER_END) { ssl->session->ticketNonce.len = nonceLen; XMEMCPY(ssl->session->ticketNonce.data, exp + idx, nonceLen); } #endif idx += DEF_TICKET_NONCE_SZ; /* The imported connection is past its handshake: received KeyUpdate * messages must pass the out-of-order sanity check. */ if (ssl->options.handShakeDone) ssl->msgsReceived.got_finished = 1; WOLFSSL_LEAVE("ImportTls13State", (int)idx); return (int)idx; } #endif /* WOLFSSL_SESSION_EXPORT */ /* Length of the finished label. */ #define FINISHED_LABEL_SZ 8 /* Finished label for generating finished key. */ static const byte finishedLabel[FINISHED_LABEL_SZ+1] = "finished"; /* Derive the finished secret. * * ssl The SSL/TLS object. * key The key to use with the HMAC. * secret The derived secret. * side The side that we are deriving the secret for. * returns 0 on success, otherwise failure. */ static int DeriveFinishedSecret(WOLFSSL* ssl, byte* key, byte* secret, int side) { WOLFSSL_MSG("Derive Finished Secret"); return Tls13DeriveKey(ssl, secret, -1, key, finishedLabel, FINISHED_LABEL_SZ, ssl->specs.mac_algorithm, 0, side); } /* The length of the application traffic label. */ #define APP_TRAFFIC_LABEL_SZ 11 /* The application traffic label. */ static const byte appTrafficLabel[APP_TRAFFIC_LABEL_SZ + 1] = "traffic upd"; /* Update the traffic secret. * * ssl The SSL/TLS object. * secret The previous secret and derived secret. * side The side that we are deriving the secret for. * returns 0 on success, otherwise failure. */ static int DeriveTrafficSecret(WOLFSSL* ssl, byte* secret, int side) { WOLFSSL_MSG("Derive New Application Traffic Secret"); return Tls13DeriveKey(ssl, secret, -1, secret, appTrafficLabel, APP_TRAFFIC_LABEL_SZ, ssl->specs.mac_algorithm, 0, side); } static int Tls13_HKDF_Extract(WOLFSSL *ssl, byte* prk, const byte* salt, int saltLen, byte* ikm, int ikmLen, int digest) { int ret; #ifdef HAVE_PK_CALLBACKS void *cb_ctx; CallbackHKDFExtract cb; #endif #ifdef WOLFSSL_ASYNC_REINVOKE ret = Tls13KdfAsyncInit(ssl); if (ret != 0) return ret; #endif #ifdef HAVE_PK_CALLBACKS cb_ctx = ssl->HkdfExtractCtx; cb = ssl->ctx->HkdfExtractCb; if (cb != NULL) { ret = cb(prk, salt, (word32)saltLen, ikm, (word32)ikmLen, digest, cb_ctx); } else #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) if ((int)ssl->arrays->psk_keySz < 0) { ret = PSK_KEY_ERROR; } else #endif { #if !defined(HAVE_FIPS) || \ (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)) ret = wc_Tls13_HKDF_Extract_ex(prk, salt, (word32)saltLen, ikm, (word32)ikmLen, digest, ssl->heap, ssl->devId); #else ret = wc_Tls13_HKDF_Extract(prk, salt, saltLen, ikm, ikmLen, digest); (void)ssl; #endif } #ifdef WOLFSSL_ASYNC_REINVOKE if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) ret = wolfSSL_AsyncPush(ssl, &ssl->kdfAsyncDev); #endif return ret; } /* Derive the early secret using HKDF Extract. * * ssl The SSL/TLS object. */ int DeriveEarlySecret(WOLFSSL* ssl) { int ret; WOLFSSL_MSG("Derive Early Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13DeriveEarlySecret(ssl); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; #endif PRIVATE_KEY_UNLOCK(); #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ret = Tls13_HKDF_Extract(ssl, ssl->arrays->secret, NULL, 0, ssl->arrays->psk_key, (int)ssl->arrays->psk_keySz, mac2hash(ssl->specs.mac_algorithm)); #else ret = Tls13_HKDF_Extract(ssl, ssl->arrays->secret, NULL, 0, ssl->arrays->masterSecret, 0, mac2hash(ssl->specs.mac_algorithm)); #endif PRIVATE_KEY_LOCK(); return ret; } /* The length of the derived label. */ #define DERIVED_LABEL_SZ 7 /* The derived label. */ static const byte derivedLabel[DERIVED_LABEL_SZ + 1] = "derived"; /* Derive the handshake secret using HKDF Extract. * * ssl The SSL/TLS object. */ int DeriveHandshakeSecret(WOLFSSL* ssl) { byte key[WC_MAX_DIGEST_SIZE]; int ret; WOLFSSL_MSG("Derive Handshake Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13DeriveHandshakeSecret(ssl); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; #endif /* Derive-Secret(., "derived", "") per RFC 8446 Section 7.1. * Empty hash (NULL, 0) is required by the TLS 1.3 key schedule. */ ret = DeriveKeyMsg(ssl, key, -1, ssl->arrays->secret, derivedLabel, DERIVED_LABEL_SZ, NULL, 0, ssl->specs.mac_algorithm); if (ret == 0) { PRIVATE_KEY_UNLOCK(); ret = Tls13_HKDF_Extract(ssl, ssl->arrays->preMasterSecret, key, ssl->specs.hash_size, ssl->arrays->preMasterSecret, (int)ssl->arrays->preMasterSz, mac2hash(ssl->specs.mac_algorithm)); PRIVATE_KEY_LOCK(); } if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) { /* Last use of the early secret and of the PSK it was extracted from - * zeroize both. */ ForceZero(ssl->arrays->secret, SECRET_LEN); #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ForceZero(ssl->arrays->psk_key, MAX_PSK_KEY_LEN); #endif } #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("DeriveHandshakeSecret key", key, WC_MAX_DIGEST_SIZE); #endif ForceZero(key, sizeof(key)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(key, sizeof(key)); #endif return ret; } /* Derive the master secret using HKDF Extract. * * ssl The SSL/TLS object. */ int DeriveMasterSecret(WOLFSSL* ssl) { byte key[WC_MAX_DIGEST_SIZE]; int ret; WOLFSSL_MSG("Derive Master Secret"); if (ssl == NULL || ssl->arrays == NULL) { return BAD_FUNC_ARG; } #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13DeriveMasterSecret(ssl); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; #endif /* Derive-Secret(., "derived", "") per RFC 8446 Section 7.1. * Empty hash (NULL, 0) is required by the TLS 1.3 key schedule. */ ret = DeriveKeyMsg(ssl, key, -1, ssl->arrays->preMasterSecret, derivedLabel, DERIVED_LABEL_SZ, NULL, 0, ssl->specs.mac_algorithm); if (ret == 0) { PRIVATE_KEY_UNLOCK(); ret = Tls13_HKDF_Extract(ssl, ssl->arrays->masterSecret, key, ssl->specs.hash_size, ssl->arrays->masterSecret, 0, mac2hash(ssl->specs.mac_algorithm)); PRIVATE_KEY_LOCK(); } #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("DeriveMasterSecret key", key, WC_MAX_DIGEST_SIZE); #endif ForceZero(key, sizeof(key)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(key, sizeof(key)); #endif #ifdef HAVE_KEYING_MATERIAL if (ret != 0) return ret; /* Calculate exporter secret only when saving arrays */ if (ssl->options.saveArrays) ret = DeriveExporterSecret(ssl, ssl->arrays->exporterSecret); #endif return ret; } #if defined(HAVE_SESSION_TICKET) /* Length of the resumption label. */ #define RESUMPTION_LABEL_SZ 10 /* Resumption label for generating PSK associated with the ticket. */ static const byte resumptionLabel[RESUMPTION_LABEL_SZ+1] = "resumption"; /* Derive the PSK associated with the ticket. * * ssl The SSL/TLS object. * nonce The nonce to derive with. * nonceLen The length of the nonce to derive with. * secret The derived secret. * returns 0 on success, otherwise failure. */ int DeriveResumptionPSK(WOLFSSL* ssl, byte* nonce, byte nonceLen, byte* secret) { int digestAlg; /* Only one protocol version defined at this time. */ const byte* protocol = tls13ProtocolLabel; word32 protocolLen = TLS13_PROTOCOL_LABEL_SZ; int ret; WOLFSSL_MSG("Derive Resumption PSK"); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { protocol = dtls13ProtocolLabel; protocolLen = DTLS13_PROTOCOL_LABEL_SZ; } #endif /* WOLFSSL_DTLS13 */ switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: digestAlg = WC_SHA256; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: digestAlg = WC_SHA384; break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: digestAlg = WC_SHA512; break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: digestAlg = WC_SM3; break; #endif default: return BAD_FUNC_ARG; } #if defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) PRIVATE_KEY_UNLOCK(); ret = wc_Tls13_HKDF_Expand_Label_Alloc(secret, ssl->specs.hash_size, ssl->session->masterSecret, ssl->specs.hash_size, protocol, protocolLen, resumptionLabel, RESUMPTION_LABEL_SZ, nonce, nonceLen, digestAlg, ssl->heap); PRIVATE_KEY_LOCK(); #else ret = Tls13HKDFExpandLabel(ssl, secret, ssl->specs.hash_size, ssl->session->masterSecret, ssl->specs.hash_size, protocol, protocolLen, resumptionLabel, RESUMPTION_LABEL_SZ, nonce, nonceLen, digestAlg); #endif /* !defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3) */ return ret; } #endif /* HAVE_SESSION_TICKET */ /* Calculate the HMAC of message data to this point. * * ssl The SSL/TLS object. * key The HMAC key. * hash The hash result - verify data. * returns length of verify data generated. */ #if defined(WOLFSSL_ASYNC_REINVOKE) && !defined(NO_HMAC) /* Release the held transcript Hmac and its resume state. */ void Tls13FreeHsHmac(WOLFSSL* ssl) { if (ssl->hsHmac != NULL) { wc_HmacFree(ssl->hsHmac); XFREE(ssl->hsHmac, ssl->heap, DYNAMIC_TYPE_HMAC); ssl->hsHmac = NULL; } ssl->hsHmacStep = 0; ssl->hsHmacOut = NULL; } #endif /* WOLFSSL_ASYNC_REINVOKE && !NO_HMAC */ static int BuildTls13HandshakeHmac(WOLFSSL* ssl, byte* key, byte* hash, word32* pHashSz) { #ifndef WOLFSSL_ASYNC_REINVOKE WC_DECLARE_VAR(verifyHmac, Hmac, 1, 0); #endif int hashType = WC_SHA256; int hashSz = WC_SHA256_DIGEST_SIZE; int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); if (ssl == NULL || key == NULL || hash == NULL) { return BAD_FUNC_ARG; } /* Get the hash of the previous handshake messages. */ switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: hashType = WC_SHA256; hashSz = WC_SHA256_DIGEST_SIZE; ret = wc_Sha256GetHash(&ssl->hsHashes->hashSha256, hash); break; #endif /* !NO_SHA256 */ #ifdef WOLFSSL_SHA384 case sha384_mac: hashType = WC_SHA384; hashSz = WC_SHA384_DIGEST_SIZE; ret = wc_Sha384GetHash(&ssl->hsHashes->hashSha384, hash); break; #endif /* WOLFSSL_SHA384 */ #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: hashType = WC_SHA512; hashSz = WC_SHA512_DIGEST_SIZE; ret = wc_Sha512GetHash(&ssl->hsHashes->hashSha512, hash); break; #endif /* WOLFSSL_TLS13_SHA512 */ #ifdef WOLFSSL_SM3 case sm3_mac: hashType = WC_SM3; hashSz = WC_SM3_DIGEST_SIZE; ret = wc_Sm3GetHash(&ssl->hsHashes->hashSm3, hash); break; #endif /* WOLFSSL_SM3 */ default: ret = BAD_FUNC_ARG; break; } if (ret != 0) return ret; #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG(" Key"); WOLFSSL_BUFFER(key, ssl->specs.hash_size); WOLFSSL_MSG(" Msg Hash"); WOLFSSL_BUFFER(hash, hashSz); #endif #ifdef WOLFSSL_ASYNC_REINVOKE /* Held on the SSL object so a crypto callback WC_PENDING_E resumes by * re-invoking the same Hmac with identical arguments; the transcript * hash input is recomputed deterministically by the caller's retry. * Bound to the output buffer: a replayed caller that computes several * HMACs (the PSK binder list) must not resume one request against * another's key, so a different output discards the held state. */ if (ssl->hsHmac != NULL && ssl->hsHmacOut != hash) Tls13FreeHsHmac(ssl); if (ssl->hsHmac == NULL) { ssl->hsHmac = (Hmac*)XMALLOC(sizeof(Hmac), ssl->heap, DYNAMIC_TYPE_HMAC); if (ssl->hsHmac == NULL) return MEMORY_E; ret = wc_HmacInit(ssl->hsHmac, ssl->heap, ssl->devId); if (ret != 0) { Tls13FreeHsHmac(ssl); return ret; } ssl->hsHmacStep = 0; ssl->hsHmacOut = hash; } /* Armed before the operations, matching the HKDF helpers. */ ret = Tls13KdfAsyncInit(ssl); if (ret != 0) { Tls13FreeHsHmac(ssl); return ret; } if (ssl->hsHmacStep == 0) { ret = wc_HmacSetKey(ssl->hsHmac, hashType, key, ssl->specs.hash_size); if (ret == 0) ssl->hsHmacStep = 1; } if (ret == 0 && ssl->hsHmacStep == 1) { ret = wc_HmacUpdate(ssl->hsHmac, hash, (word32)hashSz); if (ret == 0) ssl->hsHmacStep = 2; } if (ret == 0 && ssl->hsHmacStep == 2) ret = wc_HmacFinal(ssl->hsHmac, hash); if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) return wolfSSL_AsyncPush(ssl, &ssl->kdfAsyncDev); Tls13FreeHsHmac(ssl); #else WC_ALLOC_VAR_EX(verifyHmac, Hmac, 1, NULL, DYNAMIC_TYPE_HMAC, return MEMORY_E); /* Calculate the verify data. */ ret = wc_HmacInit(verifyHmac, ssl->heap, ssl->devId); if (ret == 0) { ret = wc_HmacSetKey(verifyHmac, hashType, key, ssl->specs.hash_size); if (ret == 0) ret = wc_HmacUpdate(verifyHmac, hash, (word32)hashSz); if (ret == 0) ret = wc_HmacFinal(verifyHmac, hash); wc_HmacFree(verifyHmac); } WC_FREE_VAR_EX(verifyHmac, NULL, DYNAMIC_TYPE_HMAC); #endif /* WOLFSSL_ASYNC_REINVOKE */ #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG(" Hash"); WOLFSSL_BUFFER(hash, hashSz); #endif if (pHashSz) *pHashSz = (word32)hashSz; return ret; } /* The length of the label to use when deriving keys. */ #define WRITE_KEY_LABEL_SZ 3 /* The length of the label to use when deriving IVs. */ #define WRITE_IV_LABEL_SZ 2 /* The label to use when deriving keys. */ static const byte writeKeyLabel[WRITE_KEY_LABEL_SZ+1] = "key"; /* The label to use when deriving IVs. */ static const byte writeIVLabel[WRITE_IV_LABEL_SZ+1] = "iv"; /* Expand the traffic secrets held in ssl->clientSecret/serverSecret into the * record layer key and IV of ssl->keys, and, for DTLS 1.3, into the record * number key. RFC 8446 Section 7.3 and RFC 9147 Section 4.2.3. * * ssl The SSL/TLS object. * provision PROVISION_CLIENT, PROVISION_SERVER or both, naming the * direction(s) whose secret is expanded. * returns 0 on success, otherwise failure. */ int Tls13DeriveRecordKeys(WOLFSSL* ssl, int provision) { int ret = 0; int i = 0; WC_DECLARE_VAR(key_dig, byte, MAX_PRF_DIG, 0); WC_ALLOC_VAR_EX(key_dig, byte, MAX_PRF_DIG, ssl->heap, DYNAMIC_TYPE_DIGEST, return MEMORY_E); #ifdef WOLFSSL_CHECK_MEM_ZERO XMEMSET(key_dig, 0xff, MAX_PRF_DIG); wc_MemZero_Add("Tls13DeriveRecordKeys key_dig", key_dig, MAX_PRF_DIG); #endif /* Key data = client key | server key | client IV | server IV */ if (provision & PROVISION_CLIENT) { /* Derive the client key. */ WOLFSSL_MSG("Derive Client Key"); ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.key_size, ssl->clientSecret, writeKeyLabel, WRITE_KEY_LABEL_SZ, ssl->specs.mac_algorithm, 0, WOLFSSL_CLIENT_END); if (ret != 0) goto end; i += ssl->specs.key_size; } if (provision & PROVISION_SERVER) { /* Derive the server key. */ WOLFSSL_MSG("Derive Server Key"); ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.key_size, ssl->serverSecret, writeKeyLabel, WRITE_KEY_LABEL_SZ, ssl->specs.mac_algorithm, 0, WOLFSSL_SERVER_END); if (ret != 0) goto end; i += ssl->specs.key_size; } if (provision & PROVISION_CLIENT) { /* Derive the client IV. */ WOLFSSL_MSG("Derive Client IV"); ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.iv_size, ssl->clientSecret, writeIVLabel, WRITE_IV_LABEL_SZ, ssl->specs.mac_algorithm, 0, WOLFSSL_CLIENT_END); if (ret != 0) goto end; i += ssl->specs.iv_size; } if (provision & PROVISION_SERVER) { /* Derive the server IV. */ WOLFSSL_MSG("Derive Server IV"); ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.iv_size, ssl->serverSecret, writeIVLabel, WRITE_IV_LABEL_SZ, ssl->specs.mac_algorithm, 0, WOLFSSL_SERVER_END); if (ret != 0) goto end; /* Server IV is the last key material written to key_dig, so i is not * advanced here; the whole buffer is zeroed at end regardless. */ } /* Store keys and IVs but don't activate them. */ ret = StoreKeys(ssl, key_dig, provision); #ifdef WOLFSSL_DTLS13 if (ret == 0 && ssl->options.dtls) ret = Dtls13DeriveSnKeys(ssl, provision); #endif end: /* Zero the whole key_dig buffer (not just the i bytes derived) so no * key-schedule material can linger in the unused tail. */ ForceZero(key_dig, MAX_PRF_DIG); #ifdef WOLFSSL_SMALL_STACK XFREE(key_dig, ssl->heap, DYNAMIC_TYPE_DIGEST); #elif defined(WOLFSSL_CHECK_MEM_ZERO) wc_MemZero_Check(key_dig, MAX_PRF_DIG); #endif return ret; } /* Derive the keys and IVs for TLS v1.3. * * ssl The SSL/TLS object. * secret early_data_key when deriving the key and IV for encrypting early * data application data and end_of_early_data messages. * handshake_key when deriving keys and IVs for encrypting handshake * messages. * traffic_key when deriving first keys and IVs for encrypting * traffic messages. * update_traffic_key when deriving next keys and IVs for encrypting * traffic messages. * no_key when deriving keys and IVs from existing secrets without * re-deriving the secrets. Used during early data transitions. * side ENCRYPT_SIDE_ONLY when only encryption secret needs to be derived. * DECRYPT_SIDE_ONLY when only decryption secret needs to be derived. * ENCRYPT_AND_DECRYPT_SIDE when both secret needs to be derived. * store 1 indicates to derive the keys and IVs from derived secret and * store ready for provisioning. * returns 0 on success, otherwise failure. */ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store) { int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); /* Assume failure */ int provision; #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13DeriveKeys(ssl, secret, side); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { return ret; } ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); /* Assume failure */ #endif if (side == ENCRYPT_AND_DECRYPT_SIDE) { provision = PROVISION_CLIENT_SERVER; } else { provision = ((ssl->options.side != WOLFSSL_CLIENT_END) ^ (side == ENCRYPT_SIDE_ONLY)) ? PROVISION_CLIENT : PROVISION_SERVER; } /* Derive the appropriate secret to use in the HKDF. */ switch (secret) { #ifdef WOLFSSL_EARLY_DATA case early_data_key: ret = DeriveEarlyTrafficSecret(ssl, ssl->clientSecret, WOLFSSL_CLIENT_END); if (ret != 0) goto end; break; #endif case handshake_key: if (provision & PROVISION_CLIENT) { ret = DeriveClientHandshakeSecret(ssl, ssl->clientSecret); if (ret != 0) goto end; } if (provision & PROVISION_SERVER) { ret = DeriveServerHandshakeSecret(ssl, ssl->serverSecret); if (ret != 0) goto end; } break; case traffic_key: if (provision & PROVISION_CLIENT) { ret = DeriveClientTrafficSecret(ssl, ssl->clientSecret); if (ret != 0) goto end; } if (provision & PROVISION_SERVER) { ret = DeriveServerTrafficSecret(ssl, ssl->serverSecret); if (ret != 0) goto end; } break; case update_traffic_key: if (provision & PROVISION_CLIENT) { ret = DeriveTrafficSecret(ssl, ssl->clientSecret, WOLFSSL_CLIENT_END); if (ret != 0) goto end; } if (provision & PROVISION_SERVER) { ret = DeriveTrafficSecret(ssl, ssl->serverSecret, WOLFSSL_SERVER_END); if (ret != 0) goto end; } break; case no_key: /* Called with early data to derive keys from existing secrets * without re-deriving the secrets themselves. */ ret = 0; break; default: ret = BAD_FUNC_ARG; break; } #ifdef WOLFSSL_QUIC if (WOLFSSL_IS_QUIC(ssl)) { ret = wolfSSL_quic_forward_secrets(ssl, secret, side); if (ret != 0) goto end; } #endif /* WOLFSSL_QUIC */ if (!store) goto end; ret = Tls13DeriveRecordKeys(ssl, provision); #ifdef WOLFSSL_DTLS13 if (ret != 0) goto end; if (ssl->options.dtls) { w64wrapper epochNumber; switch (secret) { case early_data_key: epochNumber = w64From32(0, DTLS13_EPOCH_EARLYDATA); break; case handshake_key: epochNumber = w64From32(0, DTLS13_EPOCH_HANDSHAKE); break; case traffic_key: case no_key: epochNumber = w64From32(0, DTLS13_EPOCH_TRAFFIC0); break; case update_traffic_key: if (side == ENCRYPT_SIDE_ONLY) { epochNumber = ssl->dtls13Epoch; } else if (side == DECRYPT_SIDE_ONLY) { epochNumber = ssl->dtls13PeerEpoch; } else { ret = BAD_STATE_E; goto end; } w64Increment(&epochNumber); break; default: ret = BAD_STATE_E; goto end; } ret = Dtls13NewEpoch(ssl, epochNumber, side); if (ret != 0) goto end; } #endif /* WOLFSSL_DTLS13 */ end: if (ret != 0) { WOLFSSL_ERROR_VERBOSE(ret); } return ret; } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || defined(WOLFSSL_DTLS13) #ifdef WOLFSSL_32BIT_MILLI_TIME #ifndef NO_ASN_TIME #if defined(USER_TICKS) #if 0 word32 TimeNowInMilliseconds(void) { /* write your own clock tick function if don't want gettimeofday() needs millisecond accuracy but doesn't have to correlated to EPOCH */ } #endif #elif defined(TIME_OVERRIDES) #if !defined(NO_ASN) && !defined(NO_ASN_TIME) word32 TimeNowInMilliseconds(void) { return (word32) wc_Time(0) * 1000; } #else #ifndef HAVE_TIME_T_TYPE typedef long time_t; #endif extern time_t XTIME(time_t * timer); /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (word32) XTIME(0) * 1000; } #endif #elif defined(XTIME_MS) word32 TimeNowInMilliseconds(void) { return (word32)XTIME_MS(0); } #elif defined(USE_WINDOWS_API) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { static int init = 0; static LARGE_INTEGER freq; LARGE_INTEGER count; if (!init) { QueryPerformanceFrequency(&freq); init = 1; } QueryPerformanceCounter(&count); return (word32)(count.QuadPart / (freq.QuadPart / 1000)); } #elif defined(HAVE_RTP_SYS) #include "rtptime.h" /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (word32)rtp_get_system_sec() * 1000; } #elif defined(WOLFSSL_DEOS) word32 TimeNowInMilliseconds(void) { const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); word32 *systemTickPtr = systemTickPointer(); return (word32) (*systemTickPtr/systemTickTimeInHz) * 1000; } #elif defined(MICRIUM) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { OS_TICK ticks = 0; OS_ERR err; ticks = OSTimeGet(&err); return (word32) (ticks / OSCfg_TickRate_Hz) * 1000; } #elif defined(MICROCHIP_TCPIP_V5) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (word32) (TickGet() / (TICKS_PER_SECOND / 1000)); } #elif defined(MICROCHIP_TCPIP) #if defined(MICROCHIP_MPLAB_HARMONY) #include /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (word32)(SYS_TMR_TickCountGet() / (SYS_TMR_TickCounterFrequencyGet() / 1000)); } #else /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (word32)(SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000)); } #endif #elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { TIME_STRUCT mqxTime; _time_get_elapsed(&mqxTime); return (word32) mqxTime.SECONDS * 1000; } #elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) #include "include/task.h" /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (unsigned int)(((float)xTaskGetTickCount()) / (configTICK_RATE_HZ / 1000)); } #elif defined(FREESCALE_KSDK_BM) #include "lwip/sys.h" /* lwIP */ /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return sys_now(); } #elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) word32 TimeNowInMilliseconds(void) { return (word32)osKernelGetTickCount(); } #elif defined(WOLFSSL_TIRTOS) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (word32) Seconds_get() * 1000; } #elif defined(WOLFSSL_UTASKER) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { return (word32)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); } #elif defined(WOLFSSL_LINUXKM) word32 TimeNowInMilliseconds(void) { s64 t; #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) struct timespec ts; getnstimeofday(&ts); t = ts.tv_sec * (s64)1000; t += ts.tv_nsec / (s64)1000000; #else struct timespec64 ts; #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) ts = current_kernel_time64(); #else ktime_get_coarse_real_ts64(&ts); #endif t = ts.tv_sec * 1000L; t += ts.tv_nsec / 1000000L; #endif return (word32)t; } #elif defined(WOLFSSL_QNX_CAAM) word32 TimeNowInMilliseconds(void) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); return (word32)(now.tv_sec * 1000 + now.tv_nsec / 1000000); } #elif defined(FUSION_RTOS) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { struct timeval now; if (FCL_GETTIMEOFDAY(&now, 0) < 0) return 0; /* Convert to milliseconds number. */ return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); } #elif defined(WOLFSSL_ZEPHYR) word32 TimeNowInMilliseconds(void) { int64_t t; #if defined(CONFIG_ARCH_POSIX) k_cpu_idle(); #endif t = k_uptime_get(); /* returns current uptime in milliseconds */ return (word32)t; } #elif defined(FREERTOS) word32 TimeNowInMilliseconds(void) { return (word32)((uint64_t)(xTaskGetTickCount() * 1000) / configTICK_RATE_HZ); } #else /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ word32 TimeNowInMilliseconds(void) { struct timeval now; if (gettimeofday(&now, 0) < 0) return 0; /* Convert to milliseconds number. */ return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); } #endif #else /* user must supply time in milliseconds function: * word32 TimeNowInMilliseconds(void); * The response is milliseconds elapsed */ #endif /* !NO_ASN_TIME */ #else #ifndef NO_ASN_TIME #if defined(USER_TICKS) #if 0 sword64 TimeNowInMilliseconds(void) { /* write your own clock tick function if don't want gettimeofday() needs millisecond accuracy but doesn't have to correlated to EPOCH */ } #endif #elif defined(TIME_OVERRIDES) #if !defined(NO_ASN) && !defined(NO_ASN_TIME) sword64 TimeNowInMilliseconds(void) { return (sword64) wc_Time(0) * 1000; } #else #ifndef HAVE_TIME_T_TYPE typedef long time_t; #endif extern time_t XTIME(time_t * timer); /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 32-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64) XTIME(0) * 1000; } #endif #elif defined(XTIME_MS) sword64 TimeNowInMilliseconds(void) { return (sword64)XTIME_MS(0); } #elif defined(USE_WINDOWS_API) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { static int init = 0; static LARGE_INTEGER freq; LARGE_INTEGER count; if (!init) { QueryPerformanceFrequency(&freq); init = 1; } QueryPerformanceCounter(&count); return (sword64)(count.QuadPart / (freq.QuadPart / 1000)); } #elif defined(HAVE_RTP_SYS) #include "rtptime.h" /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64)rtp_get_system_sec() * 1000; } #elif defined(WOLFSSL_DEOS) sword64 TimeNowInMilliseconds(void) { const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); word32 *systemTickPtr = systemTickPointer(); return (sword64) (*systemTickPtr/systemTickTimeInHz) * 1000; } #elif defined(MICRIUM) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { OS_TICK ticks = 0; OS_ERR err; ticks = OSTimeGet(&err); return (sword64) (ticks / OSCfg_TickRate_Hz) * 1000; } #elif defined(MICROCHIP_TCPIP_V5) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64) (TickGet() / (TICKS_PER_SECOND / 1000)); } #elif defined(MICROCHIP_TCPIP) #if defined(MICROCHIP_MPLAB_HARMONY) #include /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64)SYS_TMR_TickCountGet() / (SYS_TMR_TickCounterFrequencyGet() / 1000); } #else /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64)SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000); } #endif #elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { TIME_STRUCT mqxTime; _time_get_elapsed(&mqxTime); return (sword64) mqxTime.SECONDS * 1000; } #elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) #include "include/task.h" /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64)xTaskGetTickCount() / (configTICK_RATE_HZ / 1000); } #elif defined(FREESCALE_KSDK_BM) #include "lwip/sys.h" /* lwIP */ /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return sys_now(); } #elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) sword64 TimeNowInMilliseconds(void) { return (sword64)osKernelGetTickCount(); } #elif defined(WOLFSSL_TIRTOS) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64) Seconds_get() * 1000; } #elif defined(WOLFSSL_UTASKER) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { return (sword64)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); } #elif defined(WOLFSSL_LINUXKM) sword64 TimeNowInMilliseconds(void) { s64 t; #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) struct timespec ts; getnstimeofday(&ts); t = ts.tv_sec * (s64)1000; t += ts.tv_nsec / (s64)1000000; #else struct timespec64 ts; #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) ts = current_kernel_time64(); #else ktime_get_coarse_real_ts64(&ts); #endif t = ts.tv_sec * 1000L; t += ts.tv_nsec / 1000000L; #endif return (sword64)t; } #elif defined(WOLFSSL_QNX_CAAM) sword64 TimeNowInMilliseconds(void) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); return (sword64)(now.tv_sec * 1000 + now.tv_nsec / 1000000); } #elif defined(FUSION_RTOS) /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { struct timeval now; if (FCL_GETTIMEOFDAY(&now, 0) < 0) return 0; /* Convert to milliseconds number. */ return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; } #elif defined(WOLFSSL_ZEPHYR) sword64 TimeNowInMilliseconds(void) { int64_t t; #if defined(CONFIG_ARCH_POSIX) k_cpu_idle(); #endif t = k_uptime_get(); /* returns current uptime in milliseconds */ return (sword64)t; } #elif defined(FREERTOS) sword64 TimeNowInMilliseconds(void) { return (sword64)((uint64_t)(xTaskGetTickCount() * 1000) / configTICK_RATE_HZ); } #else /* The time in milliseconds. * Used for tickets to represent difference between when first seen and when * sending. * * returns the time in milliseconds as a 64-bit value. */ sword64 TimeNowInMilliseconds(void) { struct timeval now; if (gettimeofday(&now, 0) < 0) return 0; /* Convert to milliseconds number. */ return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; } #endif #else /* user must supply time in milliseconds function: * sword64 TimeNowInMilliseconds(void); * The response is milliseconds elapsed */ #endif /* !NO_ASN_TIME */ #endif /* WOLFSSL_32BIT_MILLI_TIME */ #endif /* HAVE_SESSION_TICKET || !NO_PSK || WOLFSSL_DTLS13 */ /* Add record layer header to message. * * output The buffer to write the record layer header into. * length The length of the record data. * type The type of record message. * ssl The SSL/TLS object. */ static void AddTls13RecordHeader(byte* output, word32 length, byte type, WOLFSSL* ssl) { RecordLayerHeader* rl; rl = (RecordLayerHeader*)output; rl->type = type; rl->pvMajor = ssl->version.major; /* NOTE: May be TLSv1_MINOR when sending first ClientHello. */ rl->pvMinor = TLSv1_2_MINOR; c16toa((word16)length, rl->length); } /* Add handshake header to message. * * output The buffer to write the handshake header into. * length The length of the handshake data. * fragOffset The offset of the fragment data. (DTLS) * fragLength The length of the fragment data. (DTLS) * type The type of handshake message. * ssl The SSL/TLS object. (DTLS) */ static void AddTls13HandShakeHeader(byte* output, word32 length, word32 fragOffset, word32 fragLength, byte type, WOLFSSL* ssl) { HandShakeHeader* hs; (void)fragOffset; (void)fragLength; (void)ssl; #ifdef WOLFSSL_DTLS13 /* message_hash type is used for a synthetic message that replaces the first ClientHello in the hash transcript when using HelloRetryRequest. It will never be transmitted and, as the DTLS-only fields must not be considered when computing the hash transcript, we can avoid to use the DTLS handshake header. */ if (ssl->options.dtls && type != message_hash) { Dtls13HandshakeAddHeader(ssl, output, (enum HandShakeType)type, length); return; } #endif /* WOLFSSL_DTLS13 */ /* handshake header */ hs = (HandShakeHeader*)output; hs->type = type; c32to24(length, hs->length); } /* Add both record layer and handshake header to message. * * output The buffer to write the headers into. * length The length of the handshake data. * type The type of record layer message. * ssl The SSL/TLS object. (DTLS) */ static void AddTls13Headers(byte* output, word32 length, byte type, WOLFSSL* ssl) { word32 lengthAdj = HANDSHAKE_HEADER_SZ; word32 outputAdj = RECORD_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { Dtls13AddHeaders(output, length, (enum HandShakeType)type, ssl); return; } #endif /* WOLFSSL_DTLS13 */ AddTls13RecordHeader(output, length + lengthAdj, handshake, ssl); AddTls13HandShakeHeader(output + outputAdj, length, 0, length, type, ssl); } #if (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) \ && !defined(NO_CERTS) /* Add both record layer and fragment handshake header to message. * * output The buffer to write the headers into. * fragOffset The offset of the fragment data. (DTLS) * fragLength The length of the fragment data. (DTLS) * length The length of the handshake data. * type The type of record layer message. * ssl The SSL/TLS object. (DTLS) */ static void AddTls13FragHeaders(byte* output, word32 fragSz, word32 fragOffset, word32 length, byte type, WOLFSSL* ssl) { word32 lengthAdj = HANDSHAKE_HEADER_SZ; word32 outputAdj = RECORD_HEADER_SZ; (void)fragSz; #ifdef WOLFSSL_DTLS13 /* we ignore fragmentation fields here because fragmentation logic for DTLS1.3 is inside dtls13_handshake_send(). */ if (ssl->options.dtls) { Dtls13AddHeaders(output, length, (enum HandShakeType)type, ssl); return; } #endif /* WOLFSSL_DTLS13 */ AddTls13RecordHeader(output, fragSz + lengthAdj, handshake, ssl); AddTls13HandShakeHeader(output + outputAdj, length, fragOffset, fragSz, type, ssl); } #endif /* (!NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER) && !NO_CERTS */ /* Write the sequence number into the buffer. * No DTLS v1.3 support. * * ssl The SSL/TLS object. * verifyOrder Which set of sequence numbers to use. * out The buffer to write into. */ static WC_INLINE void WriteSEQTls13(WOLFSSL* ssl, int verifyOrder, byte* out) { word32 seq[2] = {0, 0}; if (ssl->options.dtls) { #ifdef WOLFSSL_DTLS13 Dtls13GetSeq(ssl, verifyOrder, seq, 1); #endif /* WOLFSSL_DTLS13 */ } else if (verifyOrder == PEER_ORDER) { seq[0] = ssl->keys.peer_sequence_number_hi; seq[1] = ssl->keys.peer_sequence_number_lo++; /* handle rollover */ if (seq[1] > ssl->keys.peer_sequence_number_lo) ssl->keys.peer_sequence_number_hi++; } else { seq[0] = ssl->keys.sequence_number_hi; seq[1] = ssl->keys.sequence_number_lo++; /* handle rollover */ if (seq[1] > ssl->keys.sequence_number_lo) ssl->keys.sequence_number_hi++; } #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG_EX("TLS 1.3 Write Sequence %d %d", seq[0], seq[1]); #endif c32toa(seq[0], out); c32toa(seq[1], out + OPAQUE32_LEN); } /* Build the nonce for TLS v1.3 encryption and decryption. * * ssl The SSL/TLS object. * nonce The nonce data to use when encrypting or decrypting. * iv The derived IV. * order The side on which the message is to be or was sent. */ static WC_INLINE void BuildTls13Nonce(WOLFSSL* ssl, byte* nonce, const byte* iv, int ivSz, int order) { int seq_offset; /* Ensure minimum nonce size for standard AEAD ciphers */ if (ivSz < AEAD_NONCE_SZ) ivSz = AEAD_NONCE_SZ; seq_offset = ivSz - SEQ_SZ; /* The nonce is the IV with the sequence XORed into the last bytes. */ WriteSEQTls13(ssl, order, nonce + seq_offset); XMEMCPY(nonce, iv, seq_offset); xorbuf(nonce + seq_offset, iv + seq_offset, SEQ_SZ); } #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) /* Encrypt with ChaCha20 and create authentication tag with Poly1305. * * ssl The SSL/TLS object. * output The buffer to write encrypted data and authentication tag into. * May be the same pointer as input. * input The data to encrypt. * sz The number of bytes to encrypt. * nonce The nonce to use with ChaCha20. * aad The additional authentication data. * aadSz The size of the addition authentication data. * tag The authentication tag buffer. * returns 0 on success, otherwise failure. */ static int ChaCha20Poly1305_Encrypt(WOLFSSL* ssl, byte* output, const byte* input, word16 sz, byte* nonce, const byte* aad, word16 aadSz, byte* tag) { /* Persistent-key stitched helper: derives the per-record Poly1305 key from * the keyed ChaCha, then encrypts and authenticates in one pass (the IFMA * stitch for large records, else two-pass). TLS 1.3 is always RFC 8439. */ return wc_ChaCha20Poly1305_Encrypt_ex(ssl->encrypt.chacha, ssl->auth.poly1305, output, input, sz, nonce, tag, aad, aadSz); } #endif #ifdef HAVE_NULL_CIPHER /* Create authentication tag and copy data over input. * * ssl The SSL/TLS object. * output The buffer to copy data into. * May be the same pointer as input. * input The data. * sz The number of bytes of data. * nonce The nonce to use with authentication. * aad The additional authentication data. * aadSz The size of the addition authentication data. * tag The authentication tag buffer. * returns 0 on success, otherwise failure. */ static int Tls13IntegrityOnly_Encrypt(WOLFSSL* ssl, byte* output, const byte* input, word16 sz, const byte* nonce, const byte* aad, word16 aadSz, byte* tag) { int ret; /* HMAC: nonce | aad | input */ ret = wc_HmacUpdate(ssl->encrypt.hmac, nonce, ssl->specs.iv_size); if (ret == 0) ret = wc_HmacUpdate(ssl->encrypt.hmac, aad, aadSz); if (ret == 0) ret = wc_HmacUpdate(ssl->encrypt.hmac, input, sz); if (ret == 0) ret = wc_HmacFinal(ssl->encrypt.hmac, tag); /* Copy the input to output if not the same buffer */ if (ret == 0 && output != input) XMEMCPY(output, input, sz); return ret; } #endif /* Encrypt data for TLS v1.3. * * ssl The SSL/TLS object. * output The buffer to write encrypted data and authentication tag into. * May be the same pointer as input. * input The record header and data to encrypt. * sz The number of bytes to encrypt. * aad The additional authentication data. * aadSz The size of the addition authentication data. * asyncOkay If non-zero can return WC_PENDING_E, otherwise blocks on crypto * returns 0 on success, otherwise failure. */ static int EncryptTls13(WOLFSSL* ssl, byte* output, const byte* input, word16 sz, const byte* aad, word16 aadSz, int asyncOkay) { int ret = 0; word16 dataSz; word16 macSz = ssl->specs.aead_mac_size; word32 nonceSz = 0; #ifdef WOLFSSL_ASYNC_CRYPT /* Only AES-GCM/AES-CCM assign asyncDev, so only they may pend under a * poll-completing device; the crypto-callback re-invoke path returns * before the push and is not limited this way. */ WC_ASYNC_DEV* asyncDev = NULL; word32 event_flags = WC_ASYNC_FLAG_CALL_AGAIN; #endif WOLFSSL_ENTER("EncryptTls13"); if (sz < ssl->specs.aead_mac_size) return BUFFER_E; dataSz = sz - ssl->specs.aead_mac_size; (void)output; (void)input; (void)sz; (void)dataSz; (void)macSz; (void)asyncOkay; (void)nonceSz; #ifdef WOLFSSL_ASYNC_CRYPT if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) { ssl->error = 0; /* clear async */ } #endif #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13AesEncrypt(ssl, output, input, dataSz); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { if (ret > 0) { ret = 0; /* tsip_Tls13AesEncrypt returns output size */ } return ret; } ret = 0; #endif /* WOLFSSL_RENESAS_TSIP_TLS */ switch (ssl->encrypt.state) { case CIPHER_STATE_BEGIN: { #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Data to encrypt"); WOLFSSL_BUFFER(input, dataSz); WOLFSSL_MSG("Additional Authentication Data"); WOLFSSL_BUFFER(aad, aadSz); #endif #ifdef WOLFSSL_CIPHER_TEXT_CHECK if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null && dataSz >= sizeof(ssl->encrypt.sanityCheck)) { XMEMCPY(ssl->encrypt.sanityCheck, input, sizeof(ssl->encrypt.sanityCheck)); } #endif #ifdef CIPHER_NONCE if (ssl->encrypt.nonce == NULL) { ssl->encrypt.nonce = (byte*)XMALLOC(AEAD_MAX_IMP_SZ, ssl->heap, DYNAMIC_TYPE_CIPHER); #ifdef WOLFSSL_CHECK_MEM_ZERO if (ssl->encrypt.nonce != NULL) { wc_MemZero_Add("EncryptTls13 nonce", ssl->encrypt.nonce, ssl->specs.iv_size); } #endif } if (ssl->encrypt.nonce == NULL) return MEMORY_E; BuildTls13Nonce(ssl, ssl->encrypt.nonce, ssl->keys.aead_enc_imp_IV, ssl->specs.iv_size, CUR_ORDER); #endif /* Advance state and proceed */ ssl->encrypt.state = CIPHER_STATE_DO; } FALL_THROUGH; case CIPHER_STATE_DO: { switch (ssl->specs.bulk_cipher_algorithm) { #ifdef BUILD_AESGCM case wolfssl_aes_gcm: #ifdef WOLFSSL_ASYNC_CRYPT /* initialize event */ asyncDev = &ssl->encrypt.aes->asyncDev; ret = wolfSSL_AsyncInit(ssl, asyncDev, event_flags); if (ret != 0) break; #endif nonceSz = AESGCM_NONCE_SZ; #if defined(HAVE_PK_CALLBACKS) ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); if (ssl->ctx && ssl->ctx->PerformTlsRecordProcessingCb) { ret = ssl->ctx->PerformTlsRecordProcessingCb(ssl, 1, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); } if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) #endif { #if ((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \ (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2))) ret = wc_AesGcmEncrypt(ssl->encrypt.aes, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); #else ret = wc_AesGcmSetExtIV(ssl->encrypt.aes, ssl->encrypt.nonce, nonceSz); if (ret == 0) { ret = wc_AesGcmEncrypt_ex(ssl->encrypt.aes, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); } #endif } break; #endif #ifdef HAVE_AESCCM case wolfssl_aes_ccm: #ifdef WOLFSSL_ASYNC_CRYPT /* initialize event */ asyncDev = &ssl->encrypt.aes->asyncDev; ret = wolfSSL_AsyncInit(ssl, asyncDev, event_flags); if (ret != 0) break; #endif nonceSz = AESCCM_NONCE_SZ; #if defined(HAVE_PK_CALLBACKS) ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); if (ssl->ctx && ssl->ctx->PerformTlsRecordProcessingCb) { ret = ssl->ctx->PerformTlsRecordProcessingCb(ssl, 1, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); } if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) #endif { #if ((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \ (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2))) ret = wc_AesCcmEncrypt(ssl->encrypt.aes, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); #else ret = wc_AesCcmSetNonce(ssl->encrypt.aes, ssl->encrypt.nonce, nonceSz); if (ret == 0) { ret = wc_AesCcmEncrypt_ex(ssl->encrypt.aes, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); } #endif } break; #endif #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) case wolfssl_chacha: ret = ChaCha20Poly1305_Encrypt(ssl, output, input, dataSz, ssl->encrypt.nonce, aad, aadSz, output + dataSz); break; #endif #ifdef WOLFSSL_SM4_GCM case wolfssl_sm4_gcm: nonceSz = SM4_GCM_NONCE_SZ; ret = wc_Sm4GcmEncrypt(ssl->encrypt.sm4, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); break; #endif #ifdef WOLFSSL_SM4_CCM case wolfssl_sm4_ccm: nonceSz = SM4_CCM_NONCE_SZ; ret = wc_Sm4CcmEncrypt(ssl->encrypt.sm4, output, input, dataSz, ssl->encrypt.nonce, nonceSz, output + dataSz, macSz, aad, aadSz); break; #endif #ifdef HAVE_NULL_CIPHER case wolfssl_cipher_null: ret = Tls13IntegrityOnly_Encrypt(ssl, output, input, dataSz, ssl->encrypt.nonce, aad, aadSz, output + dataSz); break; #endif default: WOLFSSL_MSG("wolfSSL Encrypt programming error"); return ENCRYPT_ERROR; } #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { /* if async is not okay, then block */ if (!asyncOkay) { #if defined(WOLFSSL_ASYNC_REINVOKE) && \ !defined(WOLF_CRYPTO_CB_ASYNC_POLL) /* wc_AsyncWait() never runs a callback: leave the state * at CIPHER_STATE_DO for the caller to re-invoke. */ return ret; #else ret = wc_AsyncWait(ret, asyncDev, event_flags); #endif } else { #if !defined(WOLFSSL_ASYNC_REINVOKE) || \ defined(WOLF_CRYPTO_CB_ASYNC_POLL) /* Poll completes into output; the resume must skip the * in-place AEAD or it would encrypt its own output. */ ssl->encrypt.state = CIPHER_STATE_END; #endif /* Else stay at CIPHER_STATE_DO: the retry must re-invoke * the callback or the record goes out unencrypted. */ return wolfSSL_AsyncPush(ssl, asyncDev); } } #endif /* Advance state */ ssl->encrypt.state = CIPHER_STATE_END; } FALL_THROUGH; case CIPHER_STATE_END: { #ifdef WOLFSSL_DEBUG_TLS #ifdef CIPHER_NONCE WOLFSSL_MSG("Nonce"); WOLFSSL_BUFFER(ssl->encrypt.nonce, ssl->specs.iv_size); #endif WOLFSSL_MSG("Encrypted data"); WOLFSSL_BUFFER(output, dataSz); WOLFSSL_MSG("Authentication Tag"); WOLFSSL_BUFFER(output + dataSz, macSz); #endif #ifdef WOLFSSL_CIPHER_TEXT_CHECK if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null && dataSz >= sizeof(ssl->encrypt.sanityCheck) && XMEMCMP(output, ssl->encrypt.sanityCheck, sizeof(ssl->encrypt.sanityCheck)) == 0) { WOLFSSL_MSG("EncryptTls13 sanity check failed! Glitch?"); return ENCRYPT_ERROR; } ForceZero(ssl->encrypt.sanityCheck, sizeof(ssl->encrypt.sanityCheck)); #endif #ifdef WOLFSSL_CHECK_MEM_ZERO if ((ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) && (output != input) && (ret == 0)) { wc_MemZero_Add("TLS 1.3 Encrypt plaintext", input, sz); } #endif #ifdef CIPHER_NONCE ForceZero(ssl->encrypt.nonce, ssl->specs.iv_size); #endif break; } default: break; } /* Reset state */ ssl->encrypt.state = CIPHER_STATE_BEGIN; return ret; } #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) /* Decrypt with ChaCha20 and check authentication tag with Poly1305. * * ssl The SSL/TLS object. * output The buffer to write decrypted data into. * May be the same pointer as input. * input The data to decrypt. * sz The number of bytes to decrypt. * nonce The nonce to use with ChaCha20. * aad The additional authentication data. * aadSz The size of the addition authentication data. * tagIn The authentication tag data from packet. * returns 0 on success, otherwise failure. */ static int ChaCha20Poly1305_Decrypt(WOLFSSL* ssl, byte* output, const byte* input, word16 sz, byte* nonce, const byte* aad, word16 aadSz, const byte* tagIn) { int ret; /* Persistent-key stitched helper: verifies the Poly1305 tag over * aad+ciphertext and decrypts in one pass (the IFMA decrypt stitch for * large records, else two-pass); it zeroes output on tag mismatch. */ ret = wc_ChaCha20Poly1305_Decrypt_ex(ssl->decrypt.chacha, ssl->auth.poly1305, output, input, sz, nonce, tagIn, aad, aadSz); if (ret == WC_NO_ERR_TRACE(MAC_CMP_FAILED_E)) { WOLFSSL_MSG("MAC did not match"); ret = VERIFY_MAC_ERROR; } return ret; } #endif #ifdef HAVE_NULL_CIPHER /* Check HMAC tag and copy over input. * * ssl The SSL/TLS object. * output The buffer to copy data into. * May be the same pointer as input. * input The data. * sz The number of bytes of data. * nonce The nonce to use with authentication. * aad The additional authentication data. * aadSz The size of the addition authentication data. * tagIn The authentication tag data from packet. * returns 0 on success, otherwise failure. */ static int Tls13IntegrityOnly_Decrypt(WOLFSSL* ssl, byte* output, const byte* input, word16 sz, const byte* nonce, const byte* aad, word16 aadSz, const byte* tagIn) { int ret; byte hmac[WC_MAX_DIGEST_SIZE]; /* HMAC: nonce | aad | input */ ret = wc_HmacUpdate(ssl->decrypt.hmac, nonce, ssl->specs.iv_size); if (ret == 0) ret = wc_HmacUpdate(ssl->decrypt.hmac, aad, aadSz); if (ret == 0) ret = wc_HmacUpdate(ssl->decrypt.hmac, input, sz); if (ret == 0) ret = wc_HmacFinal(ssl->decrypt.hmac, hmac); /* Check authentication tag matches */ if (ret == 0 && ConstantCompare(tagIn, hmac, ssl->specs.hash_size) != 0) ret = DECRYPT_ERROR; /* Copy the input to output if not the same buffer */ if (ret == 0 && output != input) XMEMCPY(output, input, sz); ForceZero(hmac, sizeof(hmac)); return ret; } #endif /* Decrypt data for TLS v1.3. * * ssl The SSL/TLS object. * output The buffer to write decrypted data into. * May be the same pointer as input. * input The data to decrypt and authentication tag. * sz The length of the encrypted data plus authentication tag. * aad The additional authentication data. * aadSz The size of the addition authentication data. * returns 0 on success, otherwise failure. */ int DecryptTls13(WOLFSSL* ssl, byte* output, const byte* input, word16 sz, const byte* aad, word16 aadSz) { int ret = 0; word16 dataSz; word16 macSz = ssl->specs.aead_mac_size; word32 nonceSz = 0; WOLFSSL_ENTER("DecryptTls13"); if (sz < ssl->specs.aead_mac_size) { return BAD_FUNC_ARG; } dataSz = sz - ssl->specs.aead_mac_size; #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13AesDecrypt(ssl, output, input, sz); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { #ifndef WOLFSSL_EARLY_DATA if (ret < 0) { ret = VERIFY_MAC_ERROR; WOLFSSL_ERROR_VERBOSE(ret); } #endif return ret; } #endif #ifdef WOLFSSL_ASYNC_CRYPT ret = wolfSSL_AsyncPop(ssl, &ssl->decrypt.state); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* check for still pending */ if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) return ret; ssl->error = 0; /* clear async */ /* let failures through so CIPHER_STATE_END logic is run */ } else #endif { /* Reset state */ ret = 0; ssl->decrypt.state = CIPHER_STATE_BEGIN; } (void)output; (void)input; (void)sz; (void)dataSz; (void)macSz; (void)nonceSz; switch (ssl->decrypt.state) { case CIPHER_STATE_BEGIN: { #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Data to decrypt"); WOLFSSL_BUFFER(input, dataSz); WOLFSSL_MSG("Additional Authentication Data"); WOLFSSL_BUFFER(aad, aadSz); WOLFSSL_MSG("Authentication tag"); WOLFSSL_BUFFER(input + dataSz, macSz); #endif #ifdef CIPHER_NONCE if (ssl->decrypt.nonce == NULL) { ssl->decrypt.nonce = (byte*)XMALLOC(AEAD_MAX_IMP_SZ, ssl->heap, DYNAMIC_TYPE_CIPHER); #ifdef WOLFSSL_CHECK_MEM_ZERO if (ssl->decrypt.nonce != NULL) { wc_MemZero_Add("DecryptTls13 nonce", ssl->decrypt.nonce, ssl->specs.iv_size); } #endif } if (ssl->decrypt.nonce == NULL) return MEMORY_E; BuildTls13Nonce(ssl, ssl->decrypt.nonce, ssl->keys.aead_dec_imp_IV, ssl->specs.iv_size, PEER_ORDER); #endif /* Advance state and proceed */ ssl->decrypt.state = CIPHER_STATE_DO; } FALL_THROUGH; case CIPHER_STATE_DO: { switch (ssl->specs.bulk_cipher_algorithm) { #ifdef BUILD_AESGCM case wolfssl_aes_gcm: #ifdef WOLFSSL_ASYNC_CRYPT /* initialize event */ ret = wolfSSL_AsyncInit(ssl, &ssl->decrypt.aes->asyncDev, WC_ASYNC_FLAG_NONE); if (ret != 0) break; #endif nonceSz = AESGCM_NONCE_SZ; #if defined(HAVE_PK_CALLBACKS) ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); if (ssl->ctx && ssl->ctx->PerformTlsRecordProcessingCb) { ret = ssl->ctx->PerformTlsRecordProcessingCb(ssl, 0, output, input, dataSz, ssl->decrypt.nonce, nonceSz, (byte *)(input + dataSz), macSz, aad, aadSz); } if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) #endif { ret = wc_AesGcmDecrypt(ssl->decrypt.aes, output, input, dataSz, ssl->decrypt.nonce, nonceSz, input + dataSz, macSz, aad, aadSz); #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { ret = wolfSSL_AsyncPush(ssl, &ssl->decrypt.aes->asyncDev); } #endif } break; #endif #ifdef HAVE_AESCCM case wolfssl_aes_ccm: #ifdef WOLFSSL_ASYNC_CRYPT /* initialize event */ ret = wolfSSL_AsyncInit(ssl, &ssl->decrypt.aes->asyncDev, WC_ASYNC_FLAG_NONE); if (ret != 0) break; #endif nonceSz = AESCCM_NONCE_SZ; #if defined(HAVE_PK_CALLBACKS) ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); if (ssl->ctx && ssl->ctx->PerformTlsRecordProcessingCb) { ret = ssl->ctx->PerformTlsRecordProcessingCb(ssl, 0, output, input, dataSz, ssl->decrypt.nonce, nonceSz, (byte *)(input + dataSz), macSz, aad, aadSz); } if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) #endif { ret = wc_AesCcmDecrypt(ssl->decrypt.aes, output, input, dataSz, ssl->decrypt.nonce, nonceSz, input + dataSz, macSz, aad, aadSz); #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { ret = wolfSSL_AsyncPush(ssl, &ssl->decrypt.aes->asyncDev); } #endif } break; #endif #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) case wolfssl_chacha: ret = ChaCha20Poly1305_Decrypt(ssl, output, input, dataSz, ssl->decrypt.nonce, aad, aadSz, input + dataSz); break; #endif #ifdef WOLFSSL_SM4_GCM case wolfssl_sm4_gcm: nonceSz = SM4_GCM_NONCE_SZ; ret = wc_Sm4GcmDecrypt(ssl->decrypt.sm4, output, input, dataSz, ssl->decrypt.nonce, nonceSz, input + dataSz, macSz, aad, aadSz); break; #endif #ifdef WOLFSSL_SM4_CCM case wolfssl_sm4_ccm: nonceSz = SM4_CCM_NONCE_SZ; ret = wc_Sm4CcmDecrypt(ssl->decrypt.sm4, output, input, dataSz, ssl->decrypt.nonce, nonceSz, input + dataSz, macSz, aad, aadSz); break; #endif #ifdef HAVE_NULL_CIPHER case wolfssl_cipher_null: ret = Tls13IntegrityOnly_Decrypt(ssl, output, input, dataSz, ssl->decrypt.nonce, aad, aadSz, input + dataSz); break; #endif default: WOLFSSL_MSG("wolfSSL Decrypt programming error"); return DECRYPT_ERROR; } #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { #if !defined(WOLFSSL_ASYNC_REINVOKE) || \ defined(WOLF_CRYPTO_CB_ASYNC_POLL) /* The poll completes the operation into the output buffer, * so the resume must not run the AEAD again: advance past * it now (the pop does not, the event is CALL_AGAIN). */ ssl->decrypt.state = CIPHER_STATE_END; #else /* Crypto callback re-invocation: leave the state at * CIPHER_STATE_DO so the retry re-enters the AEAD; * advancing would hand back the undecrypted record. */ #endif return ret; } #endif /* Advance state */ ssl->decrypt.state = CIPHER_STATE_END; } FALL_THROUGH; case CIPHER_STATE_END: { #ifdef WOLFSSL_DEBUG_TLS #ifdef CIPHER_NONCE WOLFSSL_MSG("Nonce"); WOLFSSL_BUFFER(ssl->decrypt.nonce, ssl->specs.iv_size); #endif WOLFSSL_MSG("Decrypted data"); WOLFSSL_BUFFER(output, dataSz); #endif #ifdef WOLFSSL_CHECK_MEM_ZERO if ((ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) && (ret == 0)) { wc_MemZero_Add("TLS 1.3 Decrypted data", output, sz); } #endif #ifdef CIPHER_NONCE ForceZero(ssl->decrypt.nonce, ssl->specs.iv_size); #endif break; } default: break; } if (ret < 0) { WOLFSSL_ERROR_VERBOSE(ret); } return ret; } /* Build SSL Message, encrypted. * TLS v1.3 encryption is AEAD only. * * ssl The SSL/TLS object. * output The buffer to write record message to. * outSz Size of the buffer being written into. * input The record data to encrypt (excluding record header). * inSz The size of the record data. * type The recorder header content type. * hashOutput Whether to hash the unencrypted record data. * sizeOnly Only want the size of the record message. * asyncOkay If non-zero can return WC_PENDING_E, otherwise blocks on crypto * returns the size of the encrypted record message or negative value on error. */ int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input, int inSz, int type, int hashOutput, int sizeOnly, int asyncOkay) { int ret; BuildMsgArgs* args; BuildMsgArgs lcl_args; WOLFSSL_ENTER("BuildTls13Message"); if (ssl == NULL) { return BAD_FUNC_ARG; } #ifdef WOLFSSL_ASYNC_CRYPT ret = WC_NO_PENDING_E; if (asyncOkay) { if (ssl->async == NULL) { ssl->async = (struct WOLFSSL_ASYNC*) XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap, DYNAMIC_TYPE_ASYNC); if (ssl->async == NULL) return MEMORY_E; XMEMSET(ssl->async, 0, sizeof(struct WOLFSSL_ASYNC)); } /* Not ssl->async->args: that buffer belongs to the handler that * called down into the record builder. */ args = &ssl->async->buildArgs; ret = wolfSSL_AsyncPop(ssl, &ssl->options.buildMsgState); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* Check for error */ if (ret < 0) goto exit_buildmsg; } } else #endif { args = &lcl_args; } /* buildArgs13Set, not the pop result, marks a resume: the handler * already popped the pending, and resetting on the pop result would * rebuild a half-built record (transcript/sequence advance twice). */ #ifdef WOLFSSL_ASYNC_CRYPT if (!asyncOkay || !ssl->options.buildArgs13Set) #endif { /* Note: these hit ssl->options even for a sizeOnly probe, where every * other result goes to lcl_args, so a probe destroys the resume point * of a suspended asynchronous build. wolfssl_local_GetRecordSize() is * the only sizeOnly caller and restores them; a new one must too. */ ret = 0; ssl->options.buildMsgState = BUILD_MSG_BEGIN; /* A fresh record must not inherit a suspended build's mid-way * cipher state. Not for sizeOnly probes: GetRecordSize() does not * save this field and rewinding would re-run the AEAD. */ if (!sizeOnly) ssl->encrypt.state = CIPHER_STATE_BEGIN; XMEMSET(args, 0, sizeof(BuildMsgArgs)); args->headerSz = RECORD_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) args->headerSz = Dtls13GetRlHeaderLength(ssl, 1); #endif /* WOLFSSL_DTLS13 */ args->sz = args->headerSz + (word32)inSz; args->idx = args->headerSz; } #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_NO_PENDING_E)) ret = 0; #endif switch (ssl->options.buildMsgState) { case BUILD_MSG_BEGIN: { /* catch mistaken sizeOnly parameter */ if (sizeOnly) { if (output || input) { WOLFSSL_MSG("BuildTls13Message with sizeOnly " "doesn't need input or output"); return BAD_FUNC_ARG; } } else if (output == NULL || input == NULL) { return BAD_FUNC_ARG; } /* Record layer content type at the end of record data. */ args->sz++; /* Authentication data at the end. */ args->sz += ssl->specs.aead_mac_size; #ifdef WOLFSSL_DTLS13 /* Pad to minimum length */ if (ssl->options.dtls && args->sz < (word32)Dtls13MinimumRecordLength(ssl)) { args->pad = Dtls13MinimumRecordLength(ssl) - args->sz; args->sz = Dtls13MinimumRecordLength(ssl); } #endif if (sizeOnly) return (int)args->sz; if (args->sz > (word32)outSz) { WOLFSSL_MSG("Oops, want to write past output buffer size"); return BUFFER_E; } /* Record data length. */ args->size = (word16)(args->sz - args->headerSz); /* Write/update the record header with the new size. * Always have the content type as application data for encrypted * messages in TLS v1.3. */ if (ssl->options.dtls) { #ifdef WOLFSSL_DTLS13 Dtls13RlAddCiphertextHeader(ssl, output, args->size); #endif /* WOLFSSL_DTLS13 */ } else { AddTls13RecordHeader(output, args->size, application_data, ssl); } /* TLS v1.3 can do in place encryption. */ if (input != output + args->idx) XMEMCPY(output + args->idx, input, (size_t)inSz); args->idx += (word32)inSz; #ifdef WOLFSSL_ASYNC_CRYPT /* Set only after the argument checks above cannot return any * more: an early error return must not leave the resume marker * set, or the next build would reuse stale args. */ if (asyncOkay) ssl->options.buildArgs13Set = 1; #endif ssl->options.buildMsgState = BUILD_MSG_HASH; } FALL_THROUGH; case BUILD_MSG_HASH: { if (hashOutput) { ret = HashOutput(ssl, output, (int)args->headerSz + inSz, 0); if (ret != 0) goto exit_buildmsg; } /* The real record content type goes at the end of the data. */ output[args->idx++] = (byte)type; /* Double check that any necessary padding is zero'd out */ XMEMSET(output + args->idx, 0, args->pad); args->idx += args->pad; ssl->options.buildMsgState = BUILD_MSG_ENCRYPT; } FALL_THROUGH; case BUILD_MSG_ENCRYPT: { #ifdef WOLFSSL_QUIC if (WOLFSSL_IS_QUIC(ssl)) { /* QUIC does not use encryption of the TLS Record Layer. * Return the original length + added headers * and restore it in the record header. */ AddTls13RecordHeader(output, (word32)inSz, (byte)type, ssl); ret = (int)args->headerSz + inSz; goto exit_buildmsg; } #endif #ifdef ATOMIC_USER if (ssl->ctx->MacEncryptCb) { /* User Record Layer Callback handling */ byte* mac = output + args->idx; output += args->headerSz; ret = ssl->ctx->MacEncryptCb(ssl, mac, output, (unsigned int)inSz, (byte)type, 0, output, output, args->size, ssl->MacEncryptCtx); } else #endif { const byte* aad = output; output += args->headerSz; ret = EncryptTls13(ssl, output, output, args->size, aad, (word16)args->headerSz, asyncOkay); #ifdef WOLFSSL_ASYNC_REINVOKE /* Non-resumable caller (alerts): finish the encryption by * re-invoking; devices were already waited on above. */ if (!asyncOkay) { int reinvoke = 0; while (ret == WC_NO_ERR_TRACE(WC_PENDING_E) && reinvoke++ < WOLFSSL_ASYNC_MAX_REINVOKE) { ret = EncryptTls13(ssl, output, output, args->size, aad, (word16)args->headerSz, asyncOkay); } if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { /* A device that never completes would otherwise spin * here forever, which is what wc_AsyncWait() used to * do. Report it instead. */ WOLFSSL_MSG("Crypto callback still pending after " "retry limit on a blocking record"); ret = WC_HW_WAIT_E; } } #endif if (ret != 0) { #ifdef WOLFSSL_ASYNC_CRYPT if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) #endif { /* Zeroize plaintext. */ ForceZero(output, args->size); } } #ifdef WOLFSSL_DTLS13 if (ret == 0 && ssl->options.dtls) { /* AAD points to the header. Reuse the variable */ ret = Dtls13EncryptRecordNumber(ssl, (byte*)aad, (word16)args->sz); } #endif /* WOLFSSL_DTLS13 */ } break; } default: break; } exit_buildmsg: WOLFSSL_LEAVE("BuildTls13Message", ret); #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { return ret; } #endif /* make sure build message state is reset */ ssl->options.buildMsgState = BUILD_MSG_BEGIN; /* return sz on success */ if (ret == 0) { ret = (int)args->sz; } else { WOLFSSL_ERROR_VERBOSE(ret); } /* Final cleanup */ #ifdef WOLFSSL_ASYNC_CRYPT if (asyncOkay) ssl->options.buildArgs13Set = 0; #endif return ret; } #if !defined(NO_WOLFSSL_CLIENT) || (!defined(NO_WOLFSSL_SERVER) && \ (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \ (defined(WOLFSSL_PSK_ONE_ID) || defined(WOLFSSL_PRIORITIZE_PSK))) /* Find the cipher suite in the suites set in the SSL. * * ssl SSL/TLS object. * suite Cipher suite to look for. * returns 1 when suite is found in SSL/TLS object's list and 0 otherwise. */ int FindSuiteSSL(const WOLFSSL* ssl, byte* suite) { word16 i; const Suites* suites = WOLFSSL_SUITES(ssl); for (i = 0; i < suites->suiteSz; i += 2) { if (suites->suites[i+0] == suite[0] && suites->suites[i+1] == suite[1]) { return 1; } } return 0; } #endif #ifndef NO_PSK /* Get the MAC algorithm for the TLS 1.3 cipher suite. * * @param [in] suite. * @return A value from wc_MACAlgorithm enumeration. */ byte SuiteMac(const byte* suite) { byte mac = no_mac; if (suite[0] == TLS13_BYTE) { switch (suite[1]) { #ifdef BUILD_TLS_AES_128_GCM_SHA256 case TLS_AES_128_GCM_SHA256: mac = sha256_mac; break; #endif #ifdef BUILD_TLS_CHACHA20_POLY1305_SHA256 case TLS_CHACHA20_POLY1305_SHA256: mac = sha256_mac; break; #endif #ifdef BUILD_TLS_AES_128_CCM_SHA256 case TLS_AES_128_CCM_SHA256: mac = sha256_mac; break; #endif #ifdef BUILD_TLS_AES_128_CCM_8_SHA256 case TLS_AES_128_CCM_8_SHA256: mac = sha256_mac; break; #endif #ifdef BUILD_TLS_AES_256_GCM_SHA384 case TLS_AES_256_GCM_SHA384: mac = sha384_mac; break; #endif default: break; } } #if (defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)) && \ defined(WOLFSSL_SM3) else if (suite[0] == CIPHER_BYTE) { switch (suite[1]) { #ifdef BUILD_TLS_SM4_GCM_SM3 case TLS_SM4_GCM_SM3: mac = sm3_mac; break; #endif #ifdef BUILD_TLS_SM4_CCM_SM3 case TLS_SM4_CCM_SM3: mac = sm3_mac; break; #endif default: break; } } #endif #ifdef HAVE_NULL_CIPHER else if (suite[0] == ECC_BYTE) { switch (suite[1]) { #ifdef BUILD_TLS_SHA256_SHA256 case TLS_SHA256_SHA256: mac = sha256_mac; break; #endif #ifdef BUILD_TLS_SHA384_SHA384 case TLS_SHA384_SHA384: mac = sha384_mac; break; #endif default: break; } } #endif return mac; } #endif #if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) /* Create Cookie extension using the hash of the first ClientHello. * * ssl SSL/TLS object. * hash The hash data. * hashSz The size of the hash data in bytes. * returns 0 on success, otherwise failure. */ int CreateCookieExt(const WOLFSSL* ssl, byte* hash, word16 hashSz, TLSX** exts, byte cipherSuite0, byte cipherSuite) { int ret; byte mac[WC_MAX_DIGEST_SIZE] = {0}; WC_DECLARE_VAR(cookieHmac, Hmac, 1, ssl->heap); byte cookieType = 0; byte macSz = 0; byte cookie[OPAQUE8_LEN + WC_MAX_DIGEST_SIZE + OPAQUE16_LEN * 2]; TLSX* ext; word16 cookieSz = 0; if (hash == NULL || hashSz == 0) { return BAD_FUNC_ARG; } if (ssl->buffers.tls13CookieSecret.buffer == NULL || ssl->buffers.tls13CookieSecret.length == 0) { WOLFSSL_MSG("Missing DTLS 1.3 cookie secret"); return COOKIE_ERROR; } /* Cookie Data = Hash Len | Hash | CS | KeyShare Group */ cookie[cookieSz++] = (byte)hashSz; XMEMCPY(cookie + cookieSz, hash, hashSz); cookieSz += hashSz; cookie[cookieSz++] = cipherSuite0; cookie[cookieSz++] = cipherSuite; if ((ext = TLSX_Find(*exts, TLSX_KEY_SHARE)) != NULL) { KeyShareEntry* kse = (KeyShareEntry*)ext->data; if (kse == NULL) { WOLFSSL_MSG("KeyShareEntry can't be empty when negotiating " "parameters"); return BAD_STATE_E; } c16toa(kse->group, cookie + cookieSz); cookieSz += OPAQUE16_LEN; } #ifndef NO_SHA256 cookieType = WC_SHA256; macSz = WC_SHA256_DIGEST_SIZE; #elif defined(WOLFSSL_SHA384) cookieType = WC_SHA384; macSz = WC_SHA384_DIGEST_SIZE; #elif defined(WOLFSSL_TLS13_SHA512) cookieType = WC_SHA512; macSz = WC_SHA512_DIGEST_SIZE; #elif defined(WOLFSSL_SM3) cookieType = WC_SM3; macSz = WC_SM3_DIGEST_SIZE; #else #error "No digest to available to use with HMAC for cookies." #endif /* NO_SHA */ 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); } 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); } } #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; /* The cookie data is the hash and the integrity check. */ return TLSX_Cookie_Use(ssl, cookie, cookieSz, mac, macSz, 1, exts); } #endif #ifdef WOLFSSL_DTLS13 #define HRR_MAX_HS_HEADER_SZ DTLS_HANDSHAKE_HEADER_SZ #else #define HRR_MAX_HS_HEADER_SZ HANDSHAKE_HEADER_SZ #endif /* WOLFSSL_DTLS13 */ static int CreateCookie(const WOLFSSL* ssl, byte** hash, byte* hashSz, Hashes* hashes, TLSX** exts) { int ret = 0; (void)exts; *hash = NULL; switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: *hash = hashes->sha256; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: *hash = hashes->sha384; break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: *hash = hashes->sha512; break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: *hash = hashes->sm3; break; #endif } *hashSz = ssl->specs.hash_size; /* check hash */ if (*hash == NULL && *hashSz > 0) return BAD_FUNC_ARG; #if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) if (ssl->options.sendCookie && ssl->options.side == WOLFSSL_SERVER_END) ret = CreateCookieExt(ssl, *hash, *hashSz, exts, ssl->options.cipherSuite0, ssl->options.cipherSuite); #endif return ret; } /* Restart the handshake hash with a hash of the previous messages. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ int RestartHandshakeHash(WOLFSSL* ssl) { int ret; byte header[HANDSHAKE_HEADER_SZ] = {0}; Hashes hashes; byte* hash = NULL; byte hashSz = 0; ret = BuildCertHashes(ssl, &hashes); if (ret != 0) return ret; ret = CreateCookie(ssl, &hash, &hashSz, &hashes, &ssl->extensions); if (ret != 0) return ret; #if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER) if (ssl->options.sendCookie && ssl->options.side == WOLFSSL_SERVER_END) return 0; #endif AddTls13HandShakeHeader(header, hashSz, 0, 0, message_hash, ssl); #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Restart Hash"); WOLFSSL_BUFFER(hash, hashSz); #endif ret = InitHandshakeHashes(ssl); if (ret != 0) return ret; ret = HashRaw(ssl, header, sizeof(header)); if (ret != 0) return ret; return HashRaw(ssl, hash, hashSz); } #if !defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER) /* The value in the random field of a ServerHello to indicate * HelloRetryRequest. */ static byte helloRetryRequestRandom[] = { 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C }; #endif #ifdef HAVE_ECH /* returns the index of the first supported cipher suite, -1 if none */ int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config) { int i = 0; if (!wc_HpkeKemIsSupported(config->kemId)) { WOLFSSL_MSG("ECH config: KEM not supported"); return WOLFSSL_FATAL_ERROR; } for (i = 0; i < config->numCipherSuites; i++) { if (wc_HpkeKdfIsSupported(config->cipherSuites[i].kdfId) && wc_HpkeAeadIsSupported(config->cipherSuites[i].aeadId)) { return i; } } WOLFSSL_MSG("ECH config: KDF or AEAD not supported"); return WOLFSSL_FATAL_ERROR; } /* Hash the inner client hello, initializing the hsHashesEch field if needed. * This should receive the client hello without outer_extensions 'encoding' * * ssl SSL/TLS object. * ech ECH object. * returns 0 on success and otherwise failure. */ static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech) { int ret = 0; int headerSz; word32 realSz; HS_Hashes* tmpHashes; #ifndef NO_WOLFSSL_CLIENT byte falseHeader[HRR_MAX_HS_HEADER_SZ]; #endif if (ssl == NULL || ech == NULL) { return BAD_FUNC_ARG; } #ifdef WOLFSSL_DTLS13 headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ : HANDSHAKE_HEADER_SZ; #else headerSz = HANDSHAKE_HEADER_SZ; #endif realSz = ech->innerClientHelloLen; tmpHashes = ssl->hsHashes; ssl->hsHashes = ssl->hsHashesEch; if (ssl->hsHashes == NULL) { ret = InitHandshakeHashes(ssl); if (ret == 0) { ssl->hsHashesEch = ssl->hsHashes; } } if (ret == 0) { #ifndef NO_WOLFSSL_CLIENT if (ssl->options.side == WOLFSSL_CLIENT_END) { /* client-side: innerClientHello contains body only */ AddTls13HandShakeHeader(falseHeader, realSz, 0, 0, client_hello, ssl); ret = HashRaw(ssl, falseHeader, headerSz); if (ret == 0) { ret = HashRaw(ssl, ech->innerClientHello, realSz); } } #endif #ifndef NO_WOLFSSL_SERVER if (ssl->options.side == WOLFSSL_SERVER_END) { /* server-side: innerClientHello contains header + body */ ret = HashRaw(ssl, ech->innerClientHello, headerSz + realSz); } #endif } ssl->hsHashes = tmpHashes; return ret; } /* Calculate the 8 ECH confirmation bytes. * * ssl SSL/TLS object. * label Ascii string describing ECH acceptance or rejection. * labelSz Length of label excluding NULL character. * input The buffer to calculate confirmation off of. * acceptOffset Where the 8 ECH confirmation bytes start. * helloSz Size of hello message. * isHrr Whether message is a HelloRetryRequest or not. * acceptExpanded An 8 byte array to store calculated confirmation to. * returns 0 on success and otherwise failure. */ static int EchCalcAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, const byte* input, int acceptOffset, int helloSz, byte isHrr, byte* acceptExpanded) { int ret = 0; int digestType = 0; int digestSize = 0; int hashSz = 0; int headerSz; HS_Hashes* tmpHashes; HS_Hashes* acceptHash = NULL; byte zeros[WC_MAX_DIGEST_SIZE]; byte transcriptEchConf[WC_MAX_DIGEST_SIZE]; byte clientHelloInnerHash[WC_MAX_DIGEST_SIZE]; byte expandLabelPrk[WC_MAX_DIGEST_SIZE]; byte messageHashHeader[HRR_MAX_HS_HEADER_SZ]; XMEMSET(zeros, 0, sizeof(zeros)); XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf)); XMEMSET(clientHelloInnerHash, 0, sizeof(clientHelloInnerHash)); XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("ECH PRK", expandLabelPrk, sizeof(expandLabelPrk)); #endif tmpHashes = ssl->hsHashes; ssl->hsHashes = ssl->hsHashesEch; #ifdef WOLFSSL_DTLS13 headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ : HANDSHAKE_HEADER_SZ; #else headerSz = HANDSHAKE_HEADER_SZ; #endif /* input starts at the handshake header, so the confirmation bytes must lie * inside the message that follows it */ if (acceptOffset < headerSz || helloSz < 0 || (helloSz + headerSz) < ECH_ACCEPT_CONFIRMATION_SZ || acceptOffset > (helloSz + headerSz) - ECH_ACCEPT_CONFIRMATION_SZ) { WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR); ret = BUFFER_ERROR; } if (ret == 0 && isHrr) { /* the transcript hash of ClientHelloInner1 */ ret = GetMsgHash(ssl, clientHelloInnerHash); if (ret > 0) { hashSz = ret; ret = 0; } else if (ret == 0) { ret = HASH_TYPE_E; } /* restart ECH transcript hash, similar to RestartHandshakeHash but * don't add a cookie */ if (ret == 0) { ret = InitHandshakeHashes(ssl); ssl->hsHashesEch = ssl->hsHashes; } if (ret == 0) { AddTls13HandShakeHeader(messageHashHeader, (word32)hashSz, 0, 0, message_hash, ssl); ret = HashRaw(ssl, messageHashHeader, headerSz); } if (ret == 0) { ret = HashRaw(ssl, clientHelloInnerHash, (word32)hashSz); } } /* hash with zeros for confirmation computation */ if (ret == 0) { ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashesEch, &acceptHash); } if (ret == 0) { ssl->hsHashes = acceptHash; ret = HashRaw(ssl, input, acceptOffset); } if (ret == 0) { ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ); } if (ret == 0) { ret = HashRaw(ssl, input + acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ, helloSz + headerSz - (acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ)); } /* get the modified transcript hash */ if (ret == 0) { ret = GetMsgHash(ssl, transcriptEchConf); if (ret > 0) { ret = 0; } else if (ret == 0) { ret = HASH_TYPE_E; } } /* pick the right type and size based on mac_algorithm */ if (ret == 0) { switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: digestType = WC_SHA256; digestSize = WC_SHA256_DIGEST_SIZE; break; #endif /* !NO_SHA256 */ #ifdef WOLFSSL_SHA384 case sha384_mac: digestType = WC_SHA384; digestSize = WC_SHA384_DIGEST_SIZE; break; #endif /* WOLFSSL_SHA384 */ #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: digestType = WC_SHA512; digestSize = WC_SHA512_DIGEST_SIZE; break; #endif /* WOLFSSL_TLS13_SHA512 */ #ifdef WOLFSSL_SM3 case sm3_mac: digestType = WC_SM3; digestSize = WC_SM3_DIGEST_SIZE; break; #endif /* WOLFSSL_SM3 */ default: ret = WOLFSSL_FATAL_ERROR; break; } } /* extract clientRandomInner with a key of all zeros */ if (ret == 0) { PRIVATE_KEY_UNLOCK(); #if !defined(HAVE_FIPS) || \ (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0)) ret = wc_HKDF_Extract_ex(digestType, zeros, (word32)digestSize, ssl->arrays->clientRandomInner, RAN_LEN, expandLabelPrk, ssl->heap, ssl->devId); #else ret = wc_HKDF_Extract(digestType, zeros, digestSize, ssl->arrays->clientRandomInner, RAN_LEN, expandLabelPrk); #endif PRIVATE_KEY_LOCK(); } /* tls expand with the confirmation label */ if (ret == 0) { PRIVATE_KEY_UNLOCK(); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Tls13HKDFExpandKeyLabel(ssl, acceptExpanded, ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize, dtls13ProtocolLabel, DTLS13_PROTOCOL_LABEL_SZ, label, labelSz, transcriptEchConf, (word32)digestSize, digestType, WOLFSSL_SERVER_END); } else #endif { ret = Tls13HKDFExpandKeyLabel(ssl, acceptExpanded, ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize, tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, label, labelSz, transcriptEchConf, (word32)digestSize, digestType, WOLFSSL_SERVER_END); } PRIVATE_KEY_LOCK(); } if (acceptHash != NULL) { ssl->hsHashes = acceptHash; FreeHandshakeHashes(ssl); } ssl->hsHashes = tmpHashes; ForceZero(expandLabelPrk, sizeof(expandLabelPrk)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(expandLabelPrk, sizeof(expandLabelPrk)); #endif return ret; } #endif #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) && \ (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) /* Record whether the peer's advertised algorithms permit SHA-1 signed * certificates, and release the peer's signature_algorithms_cert list. * * RFC 8446 Section 4.2.3 has signature_algorithms cover certificate signatures * when signature_algorithms_cert is absent. * * ssl The SSL/TLS object. * peerSuites The peer's signature_algorithms list. */ static void SetPeerSha1CertOk(WOLFSSL* ssl, const Suites* peerSuites) { const byte* list = NULL; word16 listSz = 0; word16 i; ssl->options.peerSha1CertOk = 0; if (ssl->certHashSigAlgoSz > 0) { list = ssl->certHashSigAlgo; listSz = ssl->certHashSigAlgoSz; } else if (peerSuites != NULL) { list = peerSuites->hashSigAlgo; listSz = peerSuites->hashSigAlgoSz; } for (i = 0; (list != NULL) && (i + 2 <= listSz); i += 2) { /* Only rsa_pkcs1_sha1, dsa_sha1 and ecdsa_sha1 carry sha_mac as the * first byte of the signature scheme. */ if (list[i] == sha_mac) { ssl->options.peerSha1CertOk = 1; break; } } /* Post-handshake auth parses its own list. */ XFREE(ssl->certHashSigAlgo, ssl->heap, DYNAMIC_TYPE_TLSX); ssl->certHashSigAlgo = NULL; ssl->certHashSigAlgoSz = 0; } #endif /* !NO_CERTS && !WOLFSSL_NO_SIGALG && (client || server) */ #ifndef NO_WOLFSSL_CLIENT #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) #if defined(OPENSSL_EXTRA) && !defined(WOLFSSL_PSK_ONE_ID) && \ !defined(NO_PSK) /** * convert mac algorithm to WOLFSSL_EVP_MD * @param mac_alg mac algorithm * @return const WOLFSSL_EVP_MD on successful, otherwise NULL */ static const WOLFSSL_EVP_MD* ssl_handshake_md(const byte mac_alg) { switch(mac_alg) { case no_mac: return NULL; #ifndef NO_MD5 case md5_mac: return wolfSSL_EVP_md5(); #endif #ifndef NO_SHA case sha_mac: return wolfSSL_EVP_sha1(); #endif #ifdef WOLFSSL_SHA224 case sha224_mac: return wolfSSL_EVP_sha224(); #endif case sha256_mac: return wolfSSL_EVP_sha256(); #ifdef WOLFSSL_SHA384 case sha384_mac: return wolfSSL_EVP_sha384(); #endif #ifdef WOLFSSL_SHA512 case sha512_mac: return wolfSSL_EVP_sha512(); #endif case rmd_mac: case blake2b_mac: WOLFSSL_MSG("no suitable EVP_MD"); return NULL; default: WOLFSSL_MSG("Unknown mac algorithm"); return NULL; } } #endif /* Setup pre-shared key based on the details in the extension data. * * ssl SSL/TLS object. * psk Pre-shared key extension data. * clientHello Whether called from client_hello construction. * returns 0 on success, PSK_KEY_ERROR when the client PSK callback fails and * other negative value on failure. */ static int SetupPskKey(WOLFSSL* ssl, PreSharedKey* psk, int clientHello) { #if defined(HAVE_SESSION_TICKET) || !defined(WOLFSSL_PSK_ONE_ID) int ret; #endif byte suite[2]; if (psk == NULL) return BAD_FUNC_ARG; if (!HaveUniqueSessionObj(ssl)) { WOLFSSL_MSG("Unable to have unique session object"); WOLFSSL_ERROR_VERBOSE(MEMORY_ERROR); return MEMORY_ERROR; } suite[0] = ssl->options.cipherSuite0; suite[1] = ssl->options.cipherSuite; #ifdef HAVE_SESSION_TICKET if (psk->resumption) { if (clientHello) { suite[0] = psk->cipherSuite0; suite[1] = psk->cipherSuite; /* Ensure cipher suite is supported or changed suite to one with * the same MAC algorithm. */ if (!FindSuiteSSL(ssl, suite)) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } ssl->options.cipherSuite0 = suite[0]; ssl->options.cipherSuite = suite[1]; /* Setting mac for binder and keys for deriving EarlyData. */ ret = SetCipherSpecs(ssl); if (ret != 0) return ret; } #ifdef WOLFSSL_EARLY_DATA if (ssl->session->maxEarlyDataSz == 0) ssl->earlyData = no_early_data; #endif /* Resumption PSK is master secret. */ ssl->arrays->psk_keySz = ssl->specs.hash_size; if ((ret = DeriveResumptionPSK(ssl, ssl->session->ticketNonce.data, ssl->session->ticketNonce.len, ssl->arrays->psk_key)) != 0) { return ret; } if (!clientHello) { /* CLIENT: using secret in ticket for peer authentication. */ ssl->options.peerAuthGood = 1; } } #endif #ifndef NO_PSK if (!psk->resumption) { /* Get the pre-shared key. */ #ifndef WOLFSSL_PSK_ONE_ID const char* cipherName = NULL; #ifdef OPENSSL_EXTRA WOLFSSL_SESSION* psksession = NULL; #endif /* Set the client identity to use. */ if (psk->identityLen > MAX_PSK_ID_LEN) return PSK_KEY_ERROR; XMEMSET(ssl->arrays->client_identity, 0, sizeof(ssl->arrays->client_identity)); XMEMCPY(ssl->arrays->client_identity, psk->identity, psk->identityLen); #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("PSK cipher suite:"); WOLFSSL_MSG(GetCipherNameInternal(psk->cipherSuite0, psk->cipherSuite)); #endif /* Get the pre-shared key. */ #ifdef OPENSSL_EXTRA if (ssl->options.session_psk_cb != NULL) { const unsigned char* id = NULL; size_t idlen = 0; const WOLFSSL_EVP_MD* handshake_md = NULL; if (ssl->msgsReceived.got_hello_retry_request >= 1) { handshake_md = ssl_handshake_md(ssl->specs.mac_algorithm); } /* OpenSSL compatible callback that gets cached session. */ if (ssl->options.session_psk_cb(ssl, handshake_md, &id, &idlen, &psksession) == 0) { wolfSSL_FreeSession(ssl->ctx, psksession); WOLFSSL_MSG("psk session callback failed"); return PSK_KEY_ERROR; } if (psksession != NULL) { if (idlen > MAX_PSK_KEY_LEN) { wolfSSL_FreeSession(ssl->ctx, psksession); WOLFSSL_MSG("psk key length is too long"); WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } ssl->arrays->psk_keySz = (word32)idlen; XMEMCPY(ssl->arrays->psk_key, id, idlen); suite[0] = psksession->cipherSuite0; suite[1] = psksession->cipherSuite; /* Not needed anymore. */ wolfSSL_FreeSession(ssl->ctx, psksession); /* Leave pointer not NULL to indicate success with callback. */ } } if (psksession != NULL) { /* Don't try other callbacks - we have an answer. */ } else #endif /* OPENSSL_EXTRA */ if (ssl->options.client_psk_cs_cb != NULL) { #ifdef WOLFSSL_PSK_MULTI_ID_PER_CS ssl->arrays->client_identity[0] = 0; #endif /* Lookup key again for next identity. */ ssl->arrays->psk_keySz = ssl->options.client_psk_cs_cb( ssl, ssl->arrays->server_hint, ssl->arrays->client_identity, MAX_PSK_ID_LEN, ssl->arrays->psk_key, MAX_PSK_KEY_LEN, GetCipherNameInternal(psk->cipherSuite0, psk->cipherSuite)); if (clientHello) { /* Use PSK cipher suite. */ ssl->options.cipherSuite0 = psk->cipherSuite0; ssl->options.cipherSuite = psk->cipherSuite; } else { byte pskCS[2]; pskCS[0] = psk->cipherSuite0; pskCS[1] = psk->cipherSuite; /* Ensure PSK and negotiated cipher suites have same hash. */ if (SuiteMac(pskCS) != SuiteMac(suite)) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } /* Negotiated cipher suite is to be used - update PSK. */ psk->cipherSuite0 = suite[0]; psk->cipherSuite = suite[1]; } } else if (ssl->options.client_psk_tls13_cb != NULL) { byte cipherSuite0; byte cipherSuite; int cipherSuiteFlags = WOLFSSL_CIPHER_SUITE_FLAG_NONE; ssl->arrays->psk_keySz = ssl->options.client_psk_tls13_cb(ssl, ssl->arrays->server_hint, ssl->arrays->client_identity, MAX_PSK_ID_LEN, ssl->arrays->psk_key, MAX_PSK_KEY_LEN, &cipherName); if (GetCipherSuiteFromName(cipherName, &cipherSuite0, &cipherSuite, NULL, NULL, &cipherSuiteFlags) != 0) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } ssl->options.cipherSuite0 = cipherSuite0; ssl->options.cipherSuite = cipherSuite; (void)cipherSuiteFlags; } else { ssl->arrays->psk_keySz = ssl->options.client_psk_cb(ssl, ssl->arrays->server_hint, ssl->arrays->client_identity, MAX_PSK_ID_LEN, ssl->arrays->psk_key, MAX_PSK_KEY_LEN); ssl->options.cipherSuite0 = TLS13_BYTE; ssl->options.cipherSuite = WOLFSSL_DEF_PSK_CIPHER; } if (ssl->arrays->psk_keySz == 0 || (ssl->arrays->psk_keySz > MAX_PSK_KEY_LEN && (int)ssl->arrays->psk_keySz != WC_NO_ERR_TRACE(USE_HW_PSK))) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } ret = SetCipherSpecs(ssl); if (ret != 0) return ret; #else /* PSK information loaded during setting of default TLS extensions. */ #endif /* !WOLFSSL_PSK_ONE_ID */ if (!clientHello && (psk->cipherSuite0 != suite[0] || psk->cipherSuite != suite[1])) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } if (!clientHello) { #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK if (ssl->options.certWithExternPsk) { /* Certificate authentication is still required. */ ssl->options.peerAuthGood = 0; } else #endif { /* CLIENT: using PSK for peer authentication. */ ssl->options.peerAuthGood = 1; } } } #endif #ifdef HAVE_SUPPORTED_CURVES if (!clientHello) { TLSX* ext; word32 modes; KeyShareEntry* kse = NULL; /* Get the PSK key exchange modes the client wants to negotiate. */ ext = TLSX_Find(ssl->extensions, TLSX_PSK_KEY_EXCHANGE_MODES); if (ext == NULL) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } modes = ext->val; ext = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); if (ext != NULL) { kse = (KeyShareEntry*)ext->data; } /* Use (EC)DHE for forward-security if possible. */ if (((modes & (1 << PSK_DHE_KE)) != 0) && (!ssl->options.noPskDheKe) && (kse != NULL) && kse->derived) { if ((kse->session != 0) && (kse->session != kse->group)) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } } else if (ssl->options.onlyPskDheKe || (ssl->options.failNoPSK && !psk->resumption)) { /* A mandatory external PSK (failNoPSK) must be combined with * (EC)DHE for forward secrecy, so reject a pure psk_ke * negotiation. Session-ticket resumption is exempt. */ WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } else if (ssl->options.noPskDheKe) { ssl->arrays->preMasterSz = 0; } } else #endif if (ssl->options.noPskDheKe) { ssl->arrays->preMasterSz = 0; } /* Derive the early secret using the PSK. */ return DeriveEarlySecret(ssl); } /* Derive and write the binders into the ClientHello in space left when * writing the Pre-Shared Key extension. * * ssl The SSL/TLS object. * output The buffer containing the ClientHello. * idx The index at the end of the completed ClientHello. * returns 0 on success and otherwise failure. */ static int WritePSKBinders(WOLFSSL* ssl, byte* output, word32 idx) { int ret; TLSX* ext; PreSharedKey* current; byte binderKey[WC_MAX_DIGEST_SIZE]; word16 len; WOLFSSL_ENTER("WritePSKBinders"); if (idx > WOLFSSL_MAX_16BIT) { return INPUT_SIZE_E; } ext = TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY); if (ext == NULL) return SANITY_MSG_E; /* Get the size of the binders to determine where to write binders. */ ret = TLSX_PreSharedKey_GetSizeBinders((PreSharedKey*)ext->data, client_hello, &len); if (ret < 0) return ret; idx -= len; /* Hash truncated ClientHello - up to binders. */ #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) ret = Dtls13HashHandshake(ssl, output + Dtls13GetRlHeaderLength(ssl, 0), (word16)idx - Dtls13GetRlHeaderLength(ssl, 0)); else #endif /* WOLFSSL_DTLS13 */ ret = HashOutput(ssl, output, (int)idx, 0); if (ret != 0) return ret; current = (PreSharedKey*)ext->data; #ifdef WOLFSSL_CHECK_MEM_ZERO if (current != NULL) { wc_MemZero_Add("WritePSKBinders binderKey", binderKey, sizeof(binderKey)); } #endif /* Calculate the binder for each identity based on previous handshake data. */ while (current != NULL) { if ((ret = SetupPskKey(ssl, current, 1)) != 0) break; #ifdef HAVE_SESSION_TICKET if (current->resumption) ret = DeriveBinderKeyResume(ssl, binderKey); #endif #ifndef NO_PSK if (!current->resumption) ret = DeriveBinderKey(ssl, binderKey); #endif if (ret != 0) break; /* Derive the Finished message secret. */ ret = DeriveFinishedSecret(ssl, binderKey, ssl->keys.client_write_MAC_secret, 0 /* neither end */); if (ret != 0) break; /* Build the HMAC of the handshake message data = binder. */ ret = BuildTls13HandshakeHmac(ssl, ssl->keys.client_write_MAC_secret, current->binder, ¤t->binderLen); if (ret != 0) break; current = current->next; } ForceZero(binderKey, sizeof(binderKey)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(binderKey, sizeof(binderKey)); #endif if (ret != 0) return ret; /* Data entered into extension, now write to message. */ ret = TLSX_PreSharedKey_WriteBinders((PreSharedKey*)ext->data, output + idx, client_hello, &len); if (ret < 0) return ret; /* Hash binders to complete the hash of the ClientHello. */ ret = HashRaw(ssl, output + idx, len); if (ret < 0) return ret; #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data) { if ((ret = SetupPskKey(ssl, (PreSharedKey*)ext->data, 1)) != 0) return ret; /* Derive early data encryption key. */ ret = DeriveTls13Keys(ssl, early_data_key, ENCRYPT_SIDE_ONLY, 1); if (ret != 0) return ret; if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0) return ret; } #endif WOLFSSL_LEAVE("WritePSKBinders", ret); return ret; } #endif static void GetTls13SessionId(WOLFSSL* ssl, byte* output, word32* idx) { if (ssl->session->sessionIDSz > 0) { /* Session resumption for old versions of protocol. */ if (ssl->session->sessionIDSz <= ID_LEN) { if (output != NULL) output[*idx] = ssl->session->sessionIDSz; (*idx)++; if (output != NULL) { XMEMCPY(output + *idx, ssl->session->sessionID, ssl->session->sessionIDSz); } *idx += ssl->session->sessionIDSz; } else { /* Invalid session ID length. Reset it. */ ssl->session->sessionIDSz = 0; if (output != NULL) output[*idx] = 0; (*idx)++; } } else { #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT if (ssl->options.tls13MiddleBoxCompat) { if (output != NULL) output[*idx] = ID_LEN; (*idx)++; if (output != NULL) XMEMCPY(output + *idx, ssl->arrays->clientRandom, ID_LEN); *idx += ID_LEN; } else #endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */ { /* TLS v1.3 does not use session id - 0 length. */ if (output != NULL) output[*idx] = 0; (*idx)++; } } } /* handle generation of TLS 1.3 client_hello (1) */ /* Send a ClientHello message to the server. * Include the information required to start a handshake with servers using * protocol versions less than TLS v1.3. * Only a client will send this message. * * ssl The SSL/TLS object. * returns 0 on success and otherwise failure. */ typedef struct Sch13Args { byte* output; word32 idx; int sendSz; word32 length; #if defined(HAVE_ECH) int clientRandomOffset; word32 preXLength; word32 expandedInnerLen; WOLFSSL_ECH* ech; #endif } Sch13Args; #ifdef WOLFSSL_EARLY_DATA /* Check if early data can potentially be sent. * Returns 1 if early data is possible, 0 otherwise. */ static int EarlyDataPossible(WOLFSSL* ssl) { /* Need session resumption OR PSK callback configured */ if (ssl->options.resuming) { return 1; } #ifndef NO_PSK if (ssl->options.client_psk_tls13_cb != NULL || ssl->options.client_psk_cb != NULL) { return 1; } #endif return 0; } #endif /* WOLFSSL_EARLY_DATA */ int SendTls13ClientHello(WOLFSSL* ssl) { int ret; #ifdef WOLFSSL_ASYNC_CRYPT Sch13Args* args = NULL; WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args); #else Sch13Args args[1]; #endif byte major, tls12minor; const Suites* suites; WOLFSSL_START(WC_FUNC_CLIENT_HELLO_SEND); WOLFSSL_ENTER("SendTls13ClientHello"); if (ssl == NULL) { return BAD_FUNC_ARG; } #if defined(HAVE_SECURE_RENEGOTIATION) || defined(HAVE_SERVER_RENEGOTIATION_INFO) /* Re-establish renegotiation_info advertising for a reused object * (wolfSSL_clear frees it) so a TLS 1.2 downgrade still enforces what the * ClientHello advertised. Only when absent, to keep any existing state. */ if (ssl->secure_renegotiation == NULL) { ret = SetupClientSecureRenegotiation(ssl); if (ret != WOLFSSL_SUCCESS) return ret; } #endif ssl->options.buildingMsg = 1; major = SSLv3_MAJOR; tls12minor = TLSv1_2_MINOR; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { major = DTLS_MAJOR; tls12minor = DTLSv1_2_MINOR; } #endif /* WOLFSSL_DTLS */ if (ssl->options.resuming && ssl->session->version.major != 0 && (ssl->session->version.major != ssl->version.major || ssl->session->version.minor != ssl->version.minor)) { #ifndef WOLFSSL_NO_TLS12 if (ssl->session->version.major == ssl->version.major && ssl->session->version.minor < ssl->version.minor) { /* Cannot resume with a different protocol version. */ ssl->options.resuming = 0; ssl->version.major = ssl->session->version.major; ssl->version.minor = ssl->session->version.minor; return SendClientHello(ssl); } else #endif { WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } } suites = WOLFSSL_SUITES(ssl); if (suites == NULL) { WOLFSSL_MSG("Bad suites pointer in SendTls13ClientHello"); return SUITES_ERROR; } #ifdef WOLFSSL_ASYNC_CRYPT if (ssl->async == NULL) { ssl->async = (struct WOLFSSL_ASYNC*) XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap, DYNAMIC_TYPE_ASYNC); if (ssl->async == NULL) return MEMORY_E; ssl->async->freeArgs = NULL; } args = (Sch13Args*)ssl->async->args; ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* Check for error */ if (ret < 0) return ret; } else #endif { /* Reset state */ ssl->options.asyncState = TLS_ASYNC_BEGIN; XMEMSET(args, 0, sizeof(Sch13Args)); } switch (ssl->options.asyncState) { case TLS_ASYNC_BEGIN: { word32 sessIdSz = 0; args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) args->idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; #endif /* WOLFSSL_DTLS13 */ /* Version | Random | Cipher Suites | Compression */ args->length = VERSION_SZ + RAN_LEN + suites->suiteSz + SUITE_LEN + COMP_LEN + ENUM_LEN; #ifdef WOLFSSL_QUIC if (WOLFSSL_IS_QUIC(ssl)) { /* RFC 9001 ch. 8.4 sessionID in ClientHello MUST be 0 length */ ssl->session->sessionIDSz = 0; ssl->options.tls13MiddleBoxCompat = 0; } #endif #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { /* RFC 9147 Section 5: DTLS implementations do not use the * TLS 1.3 "compatibility mode" */ ssl->options.tls13MiddleBoxCompat = 0; } #endif GetTls13SessionId(ssl, NULL, &sessIdSz); args->length += (word16)sessIdSz; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { /* legacy_cookie_id len */ args->length += ENUM_LEN; /* server sent us an HelloVerifyRequest and we allow downgrade */ if (ssl->arrays->cookieSz > 0 && ssl->options.downgrade) args->length += ssl->arrays->cookieSz; } #endif /* WOLFSSL_DTLS13 */ /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_BUILD; } /* case TLS_ASYNC_BEGIN */ FALL_THROUGH; case TLS_ASYNC_BUILD: case TLS_ASYNC_DO: { /* Auto populate extensions supported unless user defined. */ if ((ret = TLSX_PopulateExtensions(ssl, 0)) != 0) return ret; /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_FINALIZE; } /* case TLS_ASYNC_BUILD */ FALL_THROUGH; case TLS_ASYNC_FINALIZE: { #ifdef WOLFSSL_EARLY_DATA if (!EarlyDataPossible(ssl)) ssl->earlyData = no_early_data; if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) ssl->earlyData = no_early_data; if (ssl->earlyData == no_early_data) TLSX_Remove(&ssl->extensions, TLSX_EARLY_DATA, ssl->heap); if (ssl->earlyData != no_early_data && (ret = TLSX_EarlyData_Use(ssl, 0, 0)) < 0) { return ret; } #endif #ifdef WOLFSSL_QUIC if (WOLFSSL_IS_QUIC(ssl) && IsAtLeastTLSv1_3(ssl->version)) { ret = wolfSSL_quic_add_transport_extensions(ssl, client_hello); if (ret != 0) return ret; } #endif /* find length of outer and inner */ #if defined(HAVE_ECH) if (!ssl->options.disableECH) { TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH); void* hostName = NULL; word16 nameLen; if (echX == NULL) return WOLFSSL_FATAL_ERROR; args->ech = (WOLFSSL_ECH*)echX->data; if (args->ech == NULL) return WOLFSSL_FATAL_ERROR; /* if ECH was rejected by the HRR then the server MUST stop * decrypting ECH, so send a GREASE ECH for the follow-up CH */ if (ssl->echConfigs != NULL && !ssl->options.echAccepted && ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) { args->ech->state = ECH_WRITE_GREASE; } /* only prepare if we have a chance at acceptance (real ECH only) */ if (ssl->echConfigs != NULL && (ssl->options.echAccepted || args->ech->innerCount == 0)) { word32 encodedLen; byte downgrade; /* ensure that a version less than TLS1.3 is never offered */ downgrade = ssl->options.downgrade; ssl->options.downgrade = 0; /* set the type to inner */ args->ech->type = ECH_TYPE_INNER; args->preXLength = args->length; /* get expanded inner size (used for transcript) */ ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); if (ret != 0) { args->ech->type = ECH_TYPE_OUTER; ssl->options.downgrade = downgrade; return ret; } /* args->expandedInnerLen carries the length for the hash */ args->expandedInnerLen = args->length; if (args->expandedInnerLen > 0xFFFF) { args->ech->type = ECH_TYPE_OUTER; ssl->options.downgrade = downgrade; return BUFFER_E; } /* get encoded inner size */ args->ech->writeEncoded = 1; encodedLen = args->preXLength; ret = TLSX_GetRequestSize(ssl, client_hello, &encodedLen); args->ech->writeEncoded = 0; /* set the type to outer */ args->ech->type = ECH_TYPE_OUTER; ssl->options.downgrade = downgrade; if (ret != 0) return ret; /* calculate padding (RFC 9849, section 6.1.3) */ nameLen = TLSX_SNI_GetRequest(ssl->extensions, WOLFSSL_SNI_HOST_NAME, &hostName, 1); if (nameLen == 0 && ssl->ctx != NULL) nameLen = TLSX_SNI_GetRequest(ssl->ctx->extensions, WOLFSSL_SNI_HOST_NAME, &hostName, 1); if (nameLen != 0) { if (nameLen > args->ech->echConfig->maxNameLen) args->ech->paddingLen = 0; else args->ech->paddingLen = (word16)args->ech->echConfig->maxNameLen - nameLen; } else { /* maxNameLen + length of the SNI extension */ args->ech->paddingLen = args->ech->echConfig->maxNameLen + 9; } /* innerClientHelloLen and padding are based on the * encoded (sealed) inner */ args->ech->paddingLen += ECH_PADDING_TO_32(encodedLen + args->ech->paddingLen); args->ech->innerClientHelloLen = encodedLen + args->ech->paddingLen + args->ech->hpke->Nt; if (args->ech->innerClientHelloLen > 0xFFFF) return BUFFER_E; /* restore the length to pre-ClientHelloInner computations */ args->length = args->preXLength; } } #endif { #ifdef WOLFSSL_DTLS_CH_FRAG word16 maxFrag = wolfssl_local_GetMaxPlaintextSize(ssl); word16 lenWithoutExts = args->length; #endif /* Include length of TLS extensions. */ ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); if (ret != 0) return ret; /* Total message size. */ args->sendSz = (int)(args->length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) args->sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_DTLS_CH_FRAG /* Only empty the key share on the first CH; this avoids first CH * fragmentation (wolfSSL refuses them) */ if (ssl->options.dtls && args->sendSz > maxFrag && ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE) { /* Try again with an empty key share if we would be fragmenting */ ret = TLSX_KeyShare_Empty(ssl); if (ret != 0) return ret; args->length = lenWithoutExts; ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); if (ret != 0) return ret; args->sendSz = (int)(args->length + DTLS_HANDSHAKE_HEADER_SZ + DTLS_RECORD_HEADER_SZ); if (args->sendSz > maxFrag) { WOLFSSL_MSG("Can't fit first CH in one fragment."); return BUFFER_ERROR; } WOLFSSL_MSG("Sending empty key share so we don't fragment CH1"); } #endif } /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, args->sendSz)) != 0) return ret; /* Get position in output buffer to write new message to. */ args->output = GetOutputBuffer(ssl); /* Put the record and handshake headers on. */ AddTls13Headers(args->output, args->length, client_hello, ssl); /* Protocol version - negotiation now in extension: supported_versions. */ args->output[args->idx++] = major; args->output[args->idx++] = tls12minor; /* Keep for downgrade. */ ssl->chVersion = ssl->version; if (ssl->arrays == NULL) { return BAD_FUNC_ARG; } /* Client Random */ if (ssl->options.connectState == CONNECT_BEGIN) { ret = wc_RNG_GenerateBlock(ssl->rng, args->output + args->idx, RAN_LEN); if (ret != 0) return ret; /* Store random for possible second ClientHello. */ XMEMCPY(ssl->arrays->clientRandom, args->output + args->idx, RAN_LEN); } else XMEMCPY(args->output + args->idx, ssl->arrays->clientRandom, RAN_LEN); #if defined(HAVE_ECH) args->clientRandomOffset = (int)args->idx; #endif args->idx += RAN_LEN; GetTls13SessionId(ssl, args->output, &args->idx); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { args->output[args->idx++] = ssl->arrays->cookieSz; if (ssl->arrays->cookieSz > 0) { /* We have a cookie saved, so the server sent us an * HelloVerifyRequest, it means it is a v1.2 server */ if (!ssl->options.downgrade) return VERSION_ERROR; XMEMCPY(args->output + args->idx, ssl->arrays->cookie, ssl->arrays->cookieSz); args->idx += ssl->arrays->cookieSz; } } #endif /* WOLFSSL_DTLS13 */ /* Cipher suites */ c16toa(suites->suiteSz, args->output + args->idx); args->idx += OPAQUE16_LEN; XMEMCPY(args->output + args->idx, &suites->suites, suites->suiteSz); args->idx += suites->suiteSz; #ifdef WOLFSSL_DEBUG_TLS { int ii; WOLFSSL_MSG("Ciphers:"); for (ii = 0 ; ii < suites->suiteSz; ii += 2) { WOLFSSL_MSG(GetCipherNameInternal(suites->suites[ii+0], suites->suites[ii+1])); } } #endif /* Compression not supported in TLS v1.3. */ args->output[args->idx++] = COMP_LEN; args->output[args->idx++] = NO_COMPRESSION; #if defined(HAVE_ECH) /* Build the expanded inner ClientHello */ if (ssl->echConfigs != NULL && !ssl->options.disableECH && (ssl->options.echAccepted || args->ech->innerCount == 0)) { byte downgrade; /* calculate maximum buffer size needed */ word32 encodedBodyLen = args->ech->innerClientHelloLen - args->ech->hpke->Nt; word32 innerBufSize = args->expandedInnerLen; if (encodedBodyLen > innerBufSize) innerBufSize = encodedBodyLen; /* set the type to inner */ args->ech->type = ECH_TYPE_INNER; /* innerClientHello may already exist from hrr, free if it does */ if (args->ech->innerClientHello != NULL) { XFREE(args->ech->innerClientHello, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); } /* allocate the inner */ args->ech->innerClientHello = (byte*)XMALLOC(innerBufSize, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (args->ech->innerClientHello == NULL) { args->ech->type = ECH_TYPE_OUTER; return MEMORY_E; } /* copy everything before extensions into the innerClientHello * ignore record and handshake headers */ XMEMCPY(args->ech->innerClientHello, args->output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ, args->preXLength); /* copy the client random to inner - only for first CH, not after HRR */ if (!ssl->options.echAccepted) { XMEMCPY(ssl->arrays->clientRandomInner, ssl->arrays->clientRandom, RAN_LEN); } else { /* After HRR, use the same inner random as CH1 */ XMEMCPY(args->ech->innerClientHello + VERSION_SZ, ssl->arrays->clientRandomInner, RAN_LEN); } /* change the outer client random */ ret = wc_RNG_GenerateBlock(ssl->rng, args->output + args->clientRandomOffset, RAN_LEN); if (ret != 0) { args->ech->type = ECH_TYPE_OUTER; return ret; } /* copy the new client random */ XMEMCPY(ssl->arrays->clientRandom, args->output + args->clientRandomOffset, RAN_LEN); /* ensure that a version less than TLS1.3 is never offered */ downgrade = ssl->options.downgrade; ssl->options.downgrade = 0; /* write the expanded extensions into the inner buffer */ args->length = 0; ret = TLSX_WriteRequest(ssl, args->ech->innerClientHello + args->preXLength, client_hello, &args->length); if (ret != 0) { args->ech->type = ECH_TYPE_OUTER; ssl->options.downgrade = downgrade; return ret; } /* hash expanded form */ args->ech->innerClientHelloLen = args->expandedInnerLen; ret = EchHashHelloInner(ssl, args->ech); args->ech->innerClientHelloLen = encodedBodyLen + args->ech->hpke->Nt; if (ret != 0) { args->ech->type = ECH_TYPE_OUTER; ssl->options.downgrade = downgrade; return ret; } /* zero padding bytes sealed with the inner hello */ XMEMSET(args->ech->innerClientHello + args->ech->innerClientHelloLen - args->ech->hpke->Nt - args->ech->paddingLen, 0, args->ech->paddingLen); /* Rewrite inner buffer with the encoded form for sealing */ args->ech->writeEncoded = 1; args->length = 0; ret = TLSX_WriteRequest(ssl, args->ech->innerClientHello + args->preXLength, client_hello, &args->length); args->ech->writeEncoded = 0; /* set the type to outer */ args->ech->type = ECH_TYPE_OUTER; ssl->options.downgrade = downgrade; if (ret != 0) return ret; } #endif /* Write out extensions for a request. */ args->length = 0; ret = TLSX_WriteRequest(ssl, args->output + args->idx, client_hello, &args->length); if (ret != 0) return ret; args->idx += args->length; #if defined(HAVE_ECH) /* HPKE-seal inner hello and place into outer ECH extension's payload */ if (ssl->echConfigs != NULL && !ssl->options.disableECH && (ssl->options.echAccepted || args->ech->innerCount == 0)) { #if defined(WOLFSSL_TEST_ECH) if (ssl->echInnerHelloCb != NULL) { ret = ssl->echInnerHelloCb(args->ech->innerClientHello, args->ech->innerClientHelloLen - args->ech->hpke->Nt); if (ret != 0) return ret; } #endif ret = TLSX_FinalizeEch(ssl, args->ech, args->output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ, (word32)(args->sendSz - (RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ))); if (ret != 0) return ret; } /* Mark CH1 done for any ECH extension (real or GREASE) */ if (args->ech != NULL) args->ech->innerCount = 1; #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* Resumption has a specific set of extensions and binder is calculated * for each identity. */ if (TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY)) { ret = WritePSKBinders(ssl, args->output, args->idx); } else #endif { #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) ret = Dtls13HashHandshake(ssl, args->output + Dtls13GetRlHeaderLength(ssl, 0), (word16)args->idx - Dtls13GetRlHeaderLength(ssl, 0)); else #endif /* WOLFSSL_DTLS13 */ { /* compute the outer hash */ ret = HashOutput(ssl, args->output, (int)args->idx, 0); } } if (ret != 0) return ret; ssl->options.clientState = CLIENT_HELLO_COMPLETE; #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "ClientHello", handshake, args->output, args->sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) return ret; } #endif ssl->options.buildingMsg = 0; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Dtls13HandshakeSend(ssl, args->output, (word16)args->sendSz, (word16)args->idx, client_hello, 0); break; } #endif /* WOLFSSL_DTLS13 */ ssl->buffers.outputBuffer.length += (word32)args->sendSz; /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_END; } /* case TLS_ASYNC_BUILD */ FALL_THROUGH; case TLS_ASYNC_END: { #ifdef WOLFSSL_EARLY_DATA_GROUP /* QUIC needs to forward records at their encryption level * and is therefore unable to group here */ if (ssl->earlyData == no_early_data || WOLFSSL_IS_QUIC(ssl)) #endif ret = SendBuffered(ssl); break; } default: ret = INPUT_CASE_ERROR; } /* switch (ssl->options.asyncState) */ #ifdef WOLFSSL_ASYNC_CRYPT if (ret == 0) FreeAsyncCtx(ssl, 0); #endif WOLFSSL_LEAVE("SendTls13ClientHello", ret); WOLFSSL_END(WC_FUNC_CLIENT_HELLO_SEND); return ret; } #if defined(WOLFSSL_DTLS13) && !defined(NO_WOLFSSL_CLIENT) static int Dtls13ClientDoDowngrade(WOLFSSL* ssl) { int ret; if (ssl->dtls13ClientHello == NULL) return BAD_STATE_E; /* v1.3 and v1.2 hash messages to compute the transcript hash. When we are * using DTLSv1.3 we hash the first clientHello following v1.3 but the * server can negotiate a lower version. So we need to re-hash the * clientHello to adhere to DTLS <= v1.2 rules. */ ret = InitHandshakeHashes(ssl); if (ret != 0) return ret; ret = HashRaw(ssl, ssl->dtls13ClientHello, ssl->dtls13ClientHelloSz); XFREE(ssl->dtls13ClientHello, ssl->heap, DYNAMIC_TYPE_DTLS_MSG); ssl->dtls13ClientHello = NULL; ssl->dtls13ClientHelloSz = 0; ssl->keys.dtls_sequence_number_hi = (word16)w64GetHigh32(ssl->dtls13EncryptEpoch->nextSeqNumber); ssl->keys.dtls_sequence_number_lo = w64GetLow32(ssl->dtls13EncryptEpoch->nextSeqNumber); return ret; } #endif /* WOLFSSL_DTLS13 && !NO_WOLFSSL_CLIENT*/ #if defined(HAVE_ECH) /* Calculate ECH acceptance and verify the server accepted ECH. * * ssl SSL/TLS object. * label Ascii string describing ECH acceptance type. * labelSz Length of label excluding NULL character. * input The buffer to calculate confirmation off of. * acceptOffset Where the 8 ECH confirmation bytes start. * helloSz Size of hello message. * returns 0 on success and otherwise failure. */ static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, const byte* input, int acceptOffset, int helloSz, byte msgType) { int ret = 0; int headerSz; HS_Hashes* tmpHashes; byte acceptConfirmation[ECH_ACCEPT_CONFIRMATION_SZ]; XMEMSET(acceptConfirmation, 0, sizeof(acceptConfirmation)); #ifdef WOLFSSL_DTLS13 headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ : HANDSHAKE_HEADER_SZ; #else headerSz = HANDSHAKE_HEADER_SZ; #endif ret = EchCalcAcceptance(ssl, label, labelSz, input, acceptOffset, helloSz, msgType == hello_retry_request, acceptConfirmation); if (ret == 0) { tmpHashes = ssl->hsHashes; ssl->hsHashes = ssl->hsHashesEch; /* last 8 bytes must match the expand output */ ret = ConstantCompare(acceptConfirmation, input + acceptOffset, ECH_ACCEPT_CONFIRMATION_SZ); if (ret == 0) { WOLFSSL_MSG("ECH accepted"); ssl->options.echAccepted = 1; /* after HRR, hsHashesEch must contain: * message_hash(ClientHelloInner1) || HRR (actual, not zeros) */ if (msgType == hello_retry_request) { ret = HashRaw(ssl, input, helloSz + headerSz); } /* normal TLS code will calculate transcript of ServerHello */ else { ssl->hsHashes = tmpHashes; FreeHandshakeHashes(ssl); tmpHashes = ssl->hsHashesEch; ssl->hsHashesEch = NULL; } } else { if (msgType != hello_retry_request && ssl->options.echAccepted) { /* the SH has rejected ECH after the HRR has accepted it * RFC 9849, section 6.1.5 */ WOLFSSL_MSG("ECH rejected, but it was previously accepted..."); ret = INVALID_PARAMETER; } else { WOLFSSL_MSG("ECH rejected"); ret = 0; } ssl->options.echAccepted = 0; /* ECH rejected, continue with outer transcript */ FreeHandshakeHashes(ssl); ssl->hsHashesEch = NULL; } ssl->hsHashes = tmpHashes; } /* Skip only when the HRR signals ECH acceptance * -> CH2 still needs ech->extensions for inner/outer extension swap * during write */ if (ret == 0 && (msgType != hello_retry_request || !ssl->options.echAccepted)) ret = TLSX_EchReplaceExtensions(ssl, ssl->options.echAccepted); return ret; } #endif /* HAVE_ECH */ /* handle processing of TLS 1.3 server_hello (2) and hello_retry_request (6) */ /* Handle the ServerHello message from the server. * Only a client will receive this message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of ServerHello. * On exit, the index of byte after the ServerHello message. * helloSz The length of the current handshake message. * returns 0 on success and otherwise failure. */ typedef struct Dsh13Args { ProtocolVersion pv; word32 idx; word32 begin; const byte* sessId; word16 totalExtSz; byte sessIdSz; byte extMsgType; #if defined(HAVE_ECH) TLSX* echX; byte* acceptLabel; word32 acceptOffset; word16 acceptLabelSz; #endif } Dsh13Args; /* sessIdSz below bounds both the copy into arrays->sessionID and the comparison * against arrays->clientRandom, so one check only covers both while these * match. */ wc_static_assert(ID_LEN == RAN_LEN); int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 helloSz, byte* extMsgType) { int ret; byte suite[2]; byte tls12minor; #ifdef WOLFSSL_ASYNC_CRYPT Dsh13Args* args = NULL; #else Dsh13Args args[1]; #endif #ifdef WOLFSSL_ASYNC_CRYPT WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args); #endif WOLFSSL_START(WC_FUNC_SERVER_HELLO_DO); WOLFSSL_ENTER("DoTls13ServerHello"); if (ssl == NULL || ssl->arrays == NULL) return BAD_FUNC_ARG; tls12minor = TLSv1_2_MINOR; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) tls12minor = DTLSv1_2_MINOR; #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_ASYNC_CRYPT if (ssl->async == NULL) { ssl->async = (struct WOLFSSL_ASYNC*) XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap, DYNAMIC_TYPE_ASYNC); if (ssl->async == NULL) return MEMORY_E; ssl->async->freeArgs = NULL; } args = (Dsh13Args*)ssl->async->args; ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* Check for error */ if (ret < 0) { if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { /* Mark message as not received so it can process again */ ssl->msgsReceived.got_server_hello = 0; } return ret; } } else #endif { /* Reset state */ ssl->options.asyncState = TLS_ASYNC_BEGIN; XMEMSET(args, 0, sizeof(Dsh13Args)); } switch (ssl->options.asyncState) { case TLS_ASYNC_BEGIN: { byte b; #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) ssl->options.haveSupportedVersions = 0; #endif #ifdef WOLFSSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName(ssl, "ServerHello"); if (ssl->toInfoOn) AddLateName("ServerHello", &ssl->timeoutInfo); #endif /* Protocol version length check. */ if (helloSz < OPAQUE16_LEN) return BUFFER_ERROR; args->idx = *inOutIdx; args->begin = args->idx; /* Protocol version */ XMEMCPY(&args->pv, input + args->idx, OPAQUE16_LEN); args->idx += OPAQUE16_LEN; #ifdef WOLFSSL_DTLS if (ssl->options.dtls && (args->pv.major != DTLS_MAJOR || args->pv.minor == DTLS_BOGUS_MINOR)) return VERSION_ERROR; #endif /* WOLFSSL_DTLS */ #ifndef WOLFSSL_NO_TLS12 { byte wantDowngrade; wantDowngrade = args->pv.major == ssl->version.major && args->pv.minor < TLSv1_2_MINOR; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) wantDowngrade = args->pv.major == ssl->version.major && args->pv.minor > DTLSv1_2_MINOR; #endif /* WOLFSSL_DTLS13 */ if (wantDowngrade && ssl->options.downgrade) { /* Force client hello version 1.2 to work for static RSA. */ ssl->chVersion.minor = TLSv1_2_MINOR; ssl->version.minor = TLSv1_2_MINOR; ssl->options.tls1_3 = 0; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ssl->chVersion.minor = DTLSv1_2_MINOR; ssl->version.minor = DTLSv1_2_MINOR; ret = Dtls13ClientDoDowngrade(ssl); if (ret != 0) return ret; } #endif /* WOLFSSL_DTLS13 */ return DoServerHello(ssl, input, inOutIdx, helloSz); } } #endif if (args->pv.major != ssl->version.major || args->pv.minor != tls12minor) { WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } /* Random and session id length check */ if ((args->idx - args->begin) + RAN_LEN + ENUM_LEN > helloSz) return BUFFER_ERROR; /* Check if hello retry request */ if (XMEMCMP(input + args->idx, helloRetryRequestRandom, RAN_LEN) == 0) { WOLFSSL_MSG("HelloRetryRequest format"); *extMsgType = hello_retry_request; if (ssl->msgsReceived.got_hello_verify_request) { WOLFSSL_MSG("Received HelloRetryRequest after a " "HelloVerifyRequest"); WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } /* A HelloRetryRequest comes in as an ServerHello for MiddleBox compat. * Found message to be a HelloRetryRequest. * Don't allow more than one HelloRetryRequest or ServerHello. */ if (ssl->msgsReceived.got_hello_retry_request) { WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } } args->extMsgType = *extMsgType; /* Server random - keep for debugging. */ XMEMCPY(ssl->arrays->serverRandom, input + args->idx, RAN_LEN); #if defined(HAVE_ECH) /* last 8 bytes of server random */ args->acceptOffset = args->idx + RAN_LEN - ECH_ACCEPT_CONFIRMATION_SZ; #endif args->idx += RAN_LEN; /* Session id */ args->sessIdSz = input[args->idx++]; if (args->sessIdSz > ID_LEN || ((args->idx - args->begin) + args->sessIdSz > helloSz)) return BUFFER_ERROR; args->sessId = input + args->idx; args->idx += args->sessIdSz; ssl->options.haveSessionId = 1; /* Ciphersuite and compression check */ if ((args->idx - args->begin) + OPAQUE16_LEN + OPAQUE8_LEN > helloSz) return BUFFER_ERROR; /* Set the cipher suite from the message. */ ssl->options.cipherSuite0 = input[args->idx++]; ssl->options.cipherSuite = input[args->idx++]; if (*extMsgType == hello_retry_request) { ssl->options.hrrCipherSuite0 = ssl->options.cipherSuite0; ssl->options.hrrCipherSuite = ssl->options.cipherSuite; } else if (ssl->msgsReceived.got_hello_retry_request && (ssl->options.hrrCipherSuite0 != ssl->options.cipherSuite0 || ssl->options.hrrCipherSuite != ssl->options.cipherSuite)) { WOLFSSL_MSG("Received ServerHello with different cipher suite than " "HelloRetryRequest"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Chosen cipher suite:"); WOLFSSL_MSG(GetCipherNameInternal(ssl->options.cipherSuite0, ssl->options.cipherSuite)); #endif /* Compression */ b = input[args->idx++]; if (b != 0) { WOLFSSL_MSG("Must be no compression types in list"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } if ((args->idx - args->begin) + OPAQUE16_LEN > helloSz) { /* Fewer than OPAQUE16_LEN bytes remain after the compression method, so * there is no complete extensions length field. */ if ((args->idx - args->begin) < helloSz) { /* A partial extensions length field is genuinely malformed: report * it as a decode error regardless of message type. */ WOLFSSL_MSG("Truncated extensions length in ServerHello"); return BUFFER_ERROR; } /* No extensions field at all. */ if (args->extMsgType == hello_retry_request) { /* The sentinel Random (RFC 8446 4.1.3) identifies this as a TLS 1.3 * HelloRetryRequest, which MUST carry supported_versions * (4.2.1/9.2). Its complete absence is a missing mandatory * extension, so - consistently with the extensions-present case * handled later - report it as missing_extension (via * INCOMPLETE_DATA), regardless of whether a downgrade would * otherwise be allowed. Reject here before DoServerHello would * reinterpret the sentinel as a plain TLS 1.2 ServerHello.Random. */ WOLFSSL_MSG("HelloRetryRequest with no supported_versions"); WOLFSSL_ERROR_VERBOSE(INCOMPLETE_DATA); return INCOMPLETE_DATA; } if (!ssl->options.downgrade) { /* A plain ServerHello with no extensions is not offering TLS 1.3 * (no supported_versions extension - see RFC 8446 4.2.1) but TLS * 1.2 or below. This is a well-formed message, so a TLS 1.3-only * client (downgrade disabled) must reject it as a version mismatch, * not as a malformed message. Returning VERSION_ERROR makes the * caller send a protocol_version alert (RFC 8446 6.2) rather than * decode_error. */ WOLFSSL_MSG("Server offered TLS 1.2 (no supported_versions ext) " "but downgrade not allowed"); WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } #ifndef WOLFSSL_NO_TLS12 /* Force client hello version 1.2 to work for static RSA. */ ssl->chVersion.minor = TLSv1_2_MINOR; ssl->version.minor = TLSv1_2_MINOR; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ssl->chVersion.minor = DTLSv1_2_MINOR; ssl->version.minor = DTLSv1_2_MINOR; ssl->options.tls1_3 = 0; ret = Dtls13ClientDoDowngrade(ssl); if (ret != 0) return ret; } #endif /* WOLFSSL_DTLS13 */ #endif ssl->options.haveEMS = 0; if (args->pv.minor < ssl->options.minDowngrade) { WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } #ifndef WOLFSSL_NO_TLS12 ssl->options.tls1_3 = 0; return DoServerHello(ssl, input, inOutIdx, helloSz); #else WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; #endif } if ((args->idx - args->begin) < helloSz) { int foundVersion; /* Get extension length and length check. */ if ((args->idx - args->begin) + OPAQUE16_LEN > helloSz) return BUFFER_ERROR; ato16(&input[args->idx], &args->totalExtSz); args->idx += OPAQUE16_LEN; if ((args->idx - args->begin) + args->totalExtSz > helloSz) return BUFFER_ERROR; /* Need to negotiate version first. */ if ((ret = TLSX_ParseVersion(ssl, input + args->idx, args->totalExtSz, *extMsgType, &foundVersion))) { return ret; } if (!foundVersion) { /* RFC 8446 4.1.4: "The server's extensions MUST contain * 'supported_versions'." (also 9.2: "supported_versions" is * REQUIRED for all ... HelloRetryRequest messages). The HRR random * unambiguously identifies a TLS 1.3 server, so its absence is not * a downgrade attempt but a missing mandatory extension, which per * the "missing_extension" alert definition must be reported as * such. Return INCOMPLETE_DATA (which maps to a missing_extension * alert) and let the caller emit the alert via * TranslateErrorToAlert(). */ if (*extMsgType == hello_retry_request) { WOLFSSL_MSG("HelloRetryRequest missing supported_versions " "extension"); WOLFSSL_ERROR_VERBOSE(INCOMPLETE_DATA); return INCOMPLETE_DATA; } if (!ssl->options.downgrade) { WOLFSSL_MSG("Server trying to downgrade to version less than " "TLS v1.3"); WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } #if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \ defined(WOLFSSL_WPAS_SMALL) /* Check if client has disabled TLS 1.2 */ if (args->pv.minor == TLSv1_2_MINOR && (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_2) == WOLFSSL_OP_NO_TLSv1_2) { WOLFSSL_MSG("\tOption set to not allow TLSv1.2"); WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } #endif if (!ssl->options.dtls && args->pv.minor < ssl->options.minDowngrade) { WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } if (ssl->options.dtls && args->pv.minor > ssl->options.minDowngrade) { WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } ssl->version.minor = args->pv.minor; ssl->options.tls1_3 = 0; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Dtls13ClientDoDowngrade(ssl); if (ret != 0) return ret; } #endif /* WOLFSSL_DTLS13 */ } } #ifdef WOLFSSL_DTLS13 /* we are sure that version is >= v1.3 now, we can get rid of buffered * ClientHello that was buffered to re-compute the hash in case of * downgrade */ if (ssl->options.dtls && ssl->dtls13ClientHello != NULL) { XFREE(ssl->dtls13ClientHello, ssl->heap, DYNAMIC_TYPE_DTLS_MSG); ssl->dtls13ClientHello = NULL; ssl->dtls13ClientHelloSz = 0; } #endif /* WOLFSSL_DTLS13 */ /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_BUILD; } /* case TLS_ASYNC_BEGIN */ FALL_THROUGH; case TLS_ASYNC_BUILD: case TLS_ASYNC_DO: { /* restore message type */ *extMsgType = args->extMsgType; /* Parse and handle extensions, unless lower than TLS1.3. In that case, * extensions will be parsed in DoServerHello. */ if (args->totalExtSz > 0 && IsAtLeastTLSv1_3(ssl->version)) { ret = TLSX_Parse(ssl, input + args->idx, args->totalExtSz, *extMsgType, NULL); if (ret != 0) { #ifdef WOLFSSL_ASYNC_CRYPT /* Handle async operation */ if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { /* Mark message as not received so it can process again */ ssl->msgsReceived.got_server_hello = 0; } #endif return ret; } if (*extMsgType == hello_retry_request) { /* Update counts to reflect change of message type. */ ssl->msgsReceived.got_hello_retry_request = 1; ssl->msgsReceived.got_server_hello = 0; } } if (args->totalExtSz > 0) { args->idx += args->totalExtSz; } #ifdef WOLFSSL_DTLS_CID if (ssl->options.useDtlsCID && *extMsgType == server_hello) DtlsCIDOnExtensionsParsed(ssl); #endif /* WOLFSSL_DTLS_CID */ if (IsAtLeastTLSv1_3(ssl->version)) { *inOutIdx = args->idx; } ssl->options.serverState = SERVER_HELLO_COMPLETE; #ifdef HAVE_SECRET_CALLBACK if (ssl->sessionSecretCb != NULL #ifdef HAVE_SESSION_TICKET && ssl->session->ticketLen > 0 #endif ) { int secretSz = SECRET_LEN; ret = ssl->sessionSecretCb(ssl, ssl->session->masterSecret, &secretSz, ssl->sessionSecretCtx); if (ret != 0 || secretSz != SECRET_LEN) { WOLFSSL_ERROR_VERBOSE(SESSION_SECRET_CB_E); return SESSION_SECRET_CB_E; } } #endif /* HAVE_SECRET_CALLBACK */ /* Version only negotiated in extensions for TLS v1.3. * Only now do we know how to deal with session id. */ if (!IsAtLeastTLSv1_3(ssl->version)) { #ifndef WOLFSSL_NO_TLS12 ssl->arrays->sessionIDSz = args->sessIdSz; if (ssl->arrays->sessionIDSz > ID_LEN) { WOLFSSL_MSG("Invalid session ID size"); ssl->arrays->sessionIDSz = 0; return BUFFER_ERROR; } else if (ssl->arrays->sessionIDSz) { XMEMCPY(ssl->arrays->sessionID, args->sessId, ssl->arrays->sessionIDSz); ssl->options.haveSessionId = 1; } /* Force client hello version 1.2 to work for static RSA. */ if (ssl->options.dtls) ssl->chVersion.minor = DTLSv1_2_MINOR; else ssl->chVersion.minor = TLSv1_2_MINOR; /* Complete TLS v1.2 processing of ServerHello. */ ret = DoServerHello(ssl, input, inOutIdx, helloSz); #else WOLFSSL_MSG("Client using higher version, fatal error"); WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); ret = VERSION_ERROR; #endif WOLFSSL_LEAVE("DoTls13ServerHello", ret); return ret; } /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_FINALIZE; } /* case TLS_ASYNC_BUILD || TLS_ASYNC_DO */ FALL_THROUGH; case TLS_ASYNC_FINALIZE: { #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT if (ssl->options.tls13MiddleBoxCompat) { if (args->sessIdSz == 0) { WOLFSSL_MSG("args->sessIdSz == 0"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } if (ssl->session->sessionIDSz != 0) { if (ssl->session->sessionIDSz != args->sessIdSz || XMEMCMP(ssl->session->sessionID, args->sessId, args->sessIdSz) != 0) { WOLFSSL_MSG("session id doesn't match"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } } else if (XMEMCMP(ssl->arrays->clientRandom, args->sessId, args->sessIdSz) != 0) { WOLFSSL_MSG("session id doesn't match client random"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } } else #endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */ #if defined(WOLFSSL_QUIC) || defined(WOLFSSL_DTLS13) if (0 #ifdef WOLFSSL_QUIC || WOLFSSL_IS_QUIC(ssl) #endif #ifdef WOLFSSL_DTLS13 || ssl->options.dtls #endif ) { /* RFC 9147 Section 5 / RFC 9001 Section 8.4: DTLS 1.3 and QUIC * ServerHello must have empty legacy_session_id_echo. */ int requireEmptyEcho = 1; #ifdef WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID /* Compat: a wolfSSL <= 5.9.0 DTLS 1.3 server echoes the client's * legacy_session_id back instead of omitting it. */ if (ssl->options.dtls) requireEmptyEcho = 0; #endif if (requireEmptyEcho && args->sessIdSz != 0) { WOLFSSL_MSG("args->sessIdSz != 0"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } #ifdef WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID /* An echoing wolfSSL <= 5.9.0 server must still send back what was * sent (RFC 8446 Section 4.1.3), so don't accept an arbitrary value. * An empty echo is the compliant server case and stays acceptable. */ if (!requireEmptyEcho && args->sessIdSz != 0 && (args->sessIdSz != ssl->session->sessionIDSz || XMEMCMP(ssl->session->sessionID, args->sessId, args->sessIdSz) != 0)) { WOLFSSL_MSG("Server sent different session id"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } #endif } else #endif /* WOLFSSL_QUIC || WOLFSSL_DTLS13 */ if (args->sessIdSz != ssl->session->sessionIDSz || (args->sessIdSz > 0 && XMEMCMP(ssl->session->sessionID, args->sessId, args->sessIdSz) != 0)) { WOLFSSL_MSG("Server sent different session id"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } ret = SetCipherSpecs(ssl); if (ret != 0) return ret; #ifdef HAVE_NULL_CIPHER if (ssl->options.cipherSuite0 == ECC_BYTE && (ssl->options.cipherSuite == TLS_SHA256_SHA256 || ssl->options.cipherSuite == TLS_SHA384_SHA384)) { ; } else #endif #if defined(WOLFSSL_SM4_GCM) && defined(WOLFSSL_SM3) if (ssl->options.cipherSuite0 == CIPHER_BYTE && ssl->options.cipherSuite == TLS_SM4_GCM_SM3) { ; /* Do nothing. */ } else #endif #if defined(WOLFSSL_SM4_CCM) && defined(WOLFSSL_SM3) if (ssl->options.cipherSuite0 == CIPHER_BYTE && ssl->options.cipherSuite == TLS_SM4_CCM_SM3) { ; /* Do nothing. */ } else #endif /* Check that the negotiated ciphersuite matches protocol version. */ if (ssl->options.cipherSuite0 != TLS13_BYTE) { WOLFSSL_MSG("Server sent non-TLS13 cipher suite in TLS 1.3 packet"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } suite[0] = ssl->options.cipherSuite0; suite[1] = ssl->options.cipherSuite; if (!FindSuiteSSL(ssl, suite)) { WOLFSSL_MSG("Cipher suite not supported on client"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } #if defined(HAVE_ECH) /* check for acceptConfirmation */ if (ssl->echConfigs != NULL && !ssl->options.disableECH && ssl->hsHashesEch != NULL) { args->echX = TLSX_Find(ssl->extensions, TLSX_ECH); if (args->echX == NULL || args->echX->data == NULL) return WOLFSSL_FATAL_ERROR; if (args->extMsgType == hello_retry_request && ((WOLFSSL_ECH*)args->echX->data)->confBuf == NULL) { /* server rejected ECH, fall back to outer */ Free_HS_Hashes(ssl->hsHashesEch, ssl->heap); ssl->hsHashesEch = NULL; /* EchCheckAcceptance is bypassed, so replace extensions now */ ret = TLSX_EchReplaceExtensions(ssl, 0); if (ret != 0) return ret; } else { const byte* hsMsg; int acceptOffset; word32 headerSz = HANDSHAKE_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) headerSz = DTLS13_HANDSHAKE_HEADER_SZ; #endif /* EchCheckAcceptance hashes from the handshake header, which * always precedes input + args->begin (cf. HashInput) */ hsMsg = input + args->begin - headerSz; /* account for hrr extension instead of server random */ if (args->extMsgType == hello_retry_request) { acceptOffset = (int)(((WOLFSSL_ECH*)args->echX->data)->confBuf - hsMsg); args->acceptLabel = (byte*)echHrrAcceptConfirmationLabel; args->acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ; } else { acceptOffset = (int)(input + args->acceptOffset - hsMsg); args->acceptLabel = (byte*)echAcceptConfirmationLabel; args->acceptLabelSz = ECH_ACCEPT_CONFIRMATION_LABEL_SZ; } /* check acceptance */ if (ret == 0) { ret = EchCheckAcceptance(ssl, args->acceptLabel, args->acceptLabelSz, hsMsg, acceptOffset, helloSz, args->extMsgType); } if (ret != 0) return ret; /* use the inner random for client random */ if (args->extMsgType != hello_retry_request && ssl->options.echAccepted) { XMEMCPY(ssl->arrays->clientRandom, ssl->arrays->clientRandomInner, RAN_LEN); } } } #endif /* HAVE_ECH */ if (*extMsgType == server_hello) { #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) PreSharedKey* psk = NULL; TLSX* ext = TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY); if (ext != NULL) psk = (PreSharedKey*)ext->data; while (psk != NULL && !psk->chosen) psk = psk->next; if (psk == NULL) { /* A mandatory PSK is satisfied by any PSK the server chose, * including a resumption PSK - this matches the server-side * failNoPSK semantics, where a negotiated PSK (external or * resumption) is accepted. The error only fires when no PSK was * chosen at all. havePSK is only set by an external-PSK callback, * so a peer relying solely on session-ticket resumption is * unaffected. */ if (ssl->options.havePSK && ssl->options.failNoPSK) { WOLFSSL_MSG("Server did not negotiate a mandatory PSK"); WOLFSSL_ERROR_VERBOSE(PSK_MISSING_ERROR); return PSK_MISSING_ERROR; } ssl->options.resuming = 0; ssl->arrays->psk_keySz = 0; XMEMSET(ssl->arrays->psk_key, 0, MAX_PSK_KEY_LEN); } else { #if defined(HAVE_ECH) /* do not resume when outerHandshake will be negotiated */ if (ssl->echConfigs != NULL && !ssl->options.disableECH && !ssl->options.echAccepted) { WOLFSSL_MSG("ECH rejected but server negotiated PSK"); return INVALID_PARAMETER; } #endif #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK if (ssl->options.certWithExternPsk && psk->resumption) { /* RFC 9973 mode requires external PSK, not ticket resumption. */ WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } if (ssl->options.certWithExternPsk && ssl->options.shSentKeyShare == 0) { /* RFC 9973 Sect. 3: cert_with_extern_psk requires psk_dhe_ke; * a ServerHello without a key_share confirms only psk_ke. */ WOLFSSL_MSG("cert_with_extern_psk: ServerHello missing key_share"); WOLFSSL_ERROR_VERBOSE(EXT_MISSING); return EXT_MISSING; } #endif if ((ret = SetupPskKey(ssl, psk, 0)) != 0) return ret; ssl->options.pskNegotiated = 1; } #else /* no resumption possible */ ssl->options.resuming = 0; #endif /* sanity check on PSK / KSE */ if ( #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ssl->options.pskNegotiated == 0 && #endif (ssl->session->namedGroup == 0 || ssl->options.shSentKeyShare == 0)) { return EXT_MISSING; } ssl->keys.encryptionOn = 1; ssl->options.serverState = SERVER_HELLO_COMPLETE; } else { /* https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.4 * Clients MUST abort the handshake with an * "illegal_parameter" alert if the HelloRetryRequest would not result * in any change in the ClientHello. */ /* Check if the HRR contained a cookie or a keyshare */ if (!ssl->options.hrrSentKeyShare #ifdef WOLFSSL_TLS13_COOKIE && !ssl->options.hrrSentCookie #endif ) { SendAlert(ssl, alert_fatal, illegal_parameter); return EXT_MISSING; } ssl->options.tls1_3 = 1; ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE; ret = RestartHandshakeHash(ssl); } break; } /* case TLS_ASYNC_FINALIZE */ default: ret = INPUT_CASE_ERROR; } /* switch (ssl->options.asyncState) */ #ifdef WOLFSSL_ASYNC_CRYPT if (ret == 0) { FreeAsyncCtx(ssl, 0); /* Replays skip the sanity check that re-sets got_server_hello; * restore on completion (not for HRR, which re-counts it). */ if (*extMsgType == server_hello && ssl->msgsReceived.got_server_hello == 0) { ssl->msgsReceived.got_server_hello = 1; } } #endif WOLFSSL_LEAVE("DoTls13ServerHello", ret); WOLFSSL_END(WC_FUNC_SERVER_HELLO_DO); return ret; } /* handle processing TLS 1.3 encrypted_extensions (8) */ /* Parse and handle an EncryptedExtensions message. * Only a client will receive this message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of * EncryptedExtensions. * On exit, the index of byte after the EncryptedExtensions * message. * totalSz The length of the current handshake message. * returns 0 on success and otherwise failure. */ static int DoTls13EncryptedExtensions(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 totalSz) { int ret; word32 begin = *inOutIdx; word32 i = begin; word16 totalExtSz; WOLFSSL_START(WC_FUNC_ENCRYPTED_EXTENSIONS_DO); WOLFSSL_ENTER("DoTls13EncryptedExtensions"); #ifdef WOLFSSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName(ssl, "EncryptedExtensions"); if (ssl->toInfoOn) AddLateName("EncryptedExtensions", &ssl->timeoutInfo); #endif /* Length field of extension data. */ if (totalSz < OPAQUE16_LEN) return BUFFER_ERROR; ato16(&input[i], &totalExtSz); i += OPAQUE16_LEN; /* Extension data. */ if (i - begin + totalExtSz != totalSz) return BUFFER_ERROR; if ((ret = TLSX_Parse(ssl, input + i, totalExtSz, encrypted_extensions, NULL))) { return ret; } /* Move index to byte after message. */ *inOutIdx = i + totalExtSz; #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data) { TLSX* ext = TLSX_Find(ssl->extensions, TLSX_EARLY_DATA); if (ext == NULL || !ext->val) { WOLFSSL_MSG("Early data rejected by server (no early_data " "EncryptedExtensions response)"); ssl->earlyData = no_early_data; } } if (ssl->earlyData == no_early_data) { ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY); if (ret != 0) return ret; } #endif /* WOLFSSL_EARLY_DATA */ ssl->options.serverState = SERVER_ENCRYPTED_EXTENSIONS_COMPLETE; WOLFSSL_LEAVE("DoTls13EncryptedExtensions", ret); WOLFSSL_END(WC_FUNC_ENCRYPTED_EXTENSIONS_DO); return ret; } #ifndef NO_CERTS /* handle processing TLS v1.3 certificate_request (13) */ /* Handle a TLS v1.3 CertificateRequest message. * This message is always encrypted. * Only a client will receive this message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of CertificateRequest. * On exit, the index of byte after the CertificateRequest message. * size The length of the current handshake message. * returns 0 on success and otherwise failure. */ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size) { word16 len; word32 begin = *inOutIdx; int ret = 0; Suites peerSuites; #ifdef WOLFSSL_POST_HANDSHAKE_AUTH word16 reqCtxLen; const byte* reqCtxData; #endif WOLFSSL_START(WC_FUNC_CERTIFICATE_REQUEST_DO); WOLFSSL_ENTER("DoTls13CertificateRequest"); XMEMSET(&peerSuites, 0, sizeof(Suites)); #if !defined(WOLFSSL_NO_SIGALG) /* Post-handshake auth can deliver several requests; each one's cert * signature algorithms replace the last rather than adding to them. */ XFREE(ssl->certHashSigAlgo, ssl->heap, DYNAMIC_TYPE_TLSX); ssl->certHashSigAlgo = NULL; ssl->certHashSigAlgoSz = 0; #endif #ifdef WOLFSSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName(ssl, "CertificateRequest"); if (ssl->toInfoOn) AddLateName("CertificateRequest", &ssl->timeoutInfo); #endif if (OPAQUE8_LEN > size) return BUFFER_ERROR; /* Length of the request context. */ len = input[(*inOutIdx)++]; if ((*inOutIdx - begin) + len > size) return BUFFER_ERROR; /* INVALID_PARAMETER does not map to illegal_parameter in the central * alert path, so emit the alert explicitly before returning. */ if (ssl->options.connectState < FINISHED_DONE) { /* RFC 8446 Section 4.3.2: in the handshake the context is zero * length. */ if (len > 0) { SendAlert(ssl, alert_fatal, illegal_parameter); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } } #ifdef WOLFSSL_POST_HANDSHAKE_AUTH #ifdef WOLFSSL_QUIC else if (WOLFSSL_IS_QUIC(ssl)) { WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #endif else if (len == 0) { /* RFC 8446 Section 4.3.2: a post-handshake CertificateRequest context * MUST be non-empty and unique for the connection. */ SendAlert(ssl, alert_fatal, illegal_parameter); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } #endif #ifdef WOLFSSL_POST_HANDSHAKE_AUTH /* Remember the request context bytes; the CertReqCtx allocation and * linking into ssl->certReqCtx is deferred until after the rest of the * message has been validated. */ reqCtxLen = len; reqCtxData = input + *inOutIdx; /* Reject a context that duplicates one still pending on the connection. */ if (ssl->options.connectState >= FINISHED_DONE) { CertReqCtx* dup; for (dup = ssl->certReqCtx; dup != NULL; dup = dup->next) { if (dup->len == reqCtxLen && XMEMCMP(&dup->ctx, reqCtxData, reqCtxLen) == 0) { SendAlert(ssl, alert_fatal, illegal_parameter); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } } } #endif *inOutIdx += len; /* TODO: Add support for more extensions: * signed_certificate_timestamp, certificate_authorities, oid_filters. */ /* Certificate extensions */ if ((*inOutIdx - begin) + OPAQUE16_LEN > size) return BUFFER_ERROR; ato16(input + *inOutIdx, &len); *inOutIdx += OPAQUE16_LEN; if ((*inOutIdx - begin) + len > size) return BUFFER_ERROR; /* RFC 9846 Section 4.4.2: CertificateRequest.extensions has a lower bound of * 0, so an empty extensions block is parsed rather than rejected here. A * request missing the mandatory signature_algorithms extension is caught by * the check below. */ if ((ret = TLSX_Parse(ssl, input + *inOutIdx, len, certificate_request, &peerSuites))) { return ret; } *inOutIdx += len; /* No trailing bytes allowed (RFC 8446 4.3.2). */ if ((*inOutIdx - begin) != size) return BUFFER_ERROR; /* RFC 8446 Section 4.3.2: the signature_algorithms extension MUST be * present in a CertificateRequest. */ if (peerSuites.hashSigAlgoSz == 0) { SendAlert(ssl, alert_fatal, missing_extension); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) SetPeerSha1CertOk(ssl, &peerSuites); #endif #ifdef WOLFSSL_CERT_SETUP_CB if ((ret = CertSetupCbWrapper(ssl)) != 0) return ret; #endif #if defined(HAVE_ECH) /* RFC 9849 s6.1.7: ECH was offered but rejected by the server... * the client MUST respond with an empty Certificate message. */ if (ssl->echConfigs != NULL && !ssl->options.disableECH && !ssl->options.echAccepted) { ssl->options.sendVerify = SEND_BLANK_CERT; } else #endif if ((ssl->buffers.certificate && ssl->buffers.certificate->buffer && ((ssl->buffers.key && ssl->buffers.key->buffer) #ifdef HAVE_PK_CALLBACKS || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx) #endif )) #ifdef OPENSSL_EXTRA || ssl->ctx->certSetupCb != NULL #endif ) { if (PickHashSigAlgo(ssl, peerSuites.hashSigAlgo, peerSuites.hashSigAlgoSz, 0) != 0) { WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } ssl->options.sendVerify = SEND_CERT; } else { #ifndef WOLFSSL_NO_CLIENT_CERT_ERROR ssl->options.sendVerify = SEND_BLANK_CERT; #else WOLFSSL_MSG("Certificate required but none set on client"); /* RFC 8446 Section 4.4.2.4: send certificate_required when a * peer (here, the client) cannot provide a certificate that the * other peer required. */ SendAlert(ssl, alert_fatal, certificate_required); WOLFSSL_ERROR_VERBOSE(NO_CERT_ERROR); return NO_CERT_ERROR; #endif } #ifdef WOLFSSL_POST_HANDSHAKE_AUTH { /* CertReqCtx has one byte at end for context value. * Increase size to handle other implementations sending more than one byte. * That is, allocate extra space, over one byte, to hold the context value. */ CertReqCtx* certReqCtx = (CertReqCtx*)XMALLOC( sizeof(CertReqCtx) + (reqCtxLen == 0 ? 0 : reqCtxLen - 1), ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (certReqCtx == NULL) return MEMORY_E; certReqCtx->next = ssl->certReqCtx; certReqCtx->len = reqCtxLen; XMEMCPY(&certReqCtx->ctx, reqCtxData, reqCtxLen); ssl->certReqCtx = certReqCtx; } #endif WOLFSSL_LEAVE("DoTls13CertificateRequest", ret); WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_DO); return ret; } #endif /* !NO_CERTS */ #endif /* !NO_WOLFSSL_CLIENT */ #ifndef NO_WOLFSSL_SERVER #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) #ifndef NO_PSK int FindPskSuite(const WOLFSSL* ssl, PreSharedKey* psk, byte* psk_key, word32* psk_keySz, const byte* suite, int* found, byte* foundSuite) { const char* cipherName = NULL; byte cipherSuite0 = TLS13_BYTE; byte cipherSuite = WOLFSSL_DEF_PSK_CIPHER; int ret = 0; *found = 0; (void)suite; if (ssl->options.server_psk_tls13_cb != NULL) { *psk_keySz = ssl->options.server_psk_tls13_cb((WOLFSSL*)ssl, (char*)psk->identity, psk_key, MAX_PSK_KEY_LEN, &cipherName); if (*psk_keySz != 0) { int cipherSuiteFlags = WOLFSSL_CIPHER_SUITE_FLAG_NONE; *found = (GetCipherSuiteFromName(cipherName, &cipherSuite0, &cipherSuite, NULL, NULL, &cipherSuiteFlags) == 0); (void)cipherSuiteFlags; } } if (*found == 0 && (ssl->options.server_psk_cb != NULL)) { *psk_keySz = ssl->options.server_psk_cb((WOLFSSL*)ssl, (char*)psk->identity, psk_key, MAX_PSK_KEY_LEN); *found = (*psk_keySz != 0); } if (*found) { if (*psk_keySz > MAX_PSK_KEY_LEN && (int)*psk_keySz != WC_NO_ERR_TRACE(USE_HW_PSK)) { WOLFSSL_MSG("Key len too long in FindPsk()"); ret = PSK_KEY_ERROR; WOLFSSL_ERROR_VERBOSE(ret); *found = 0; } if (ret == 0) { #if !defined(WOLFSSL_PSK_ONE_ID) && !defined(WOLFSSL_PRIORITIZE_PSK) /* Check whether PSK ciphersuite is in SSL. */ *found = (suite[0] == cipherSuite0) && (suite[1] == cipherSuite); #else (void)suite; /* Check whether PSK ciphersuite is in SSL. */ { byte s[2] = { cipherSuite0, cipherSuite, }; *found = FindSuiteSSL(ssl, s); } #endif } } if (*found && foundSuite != NULL) { foundSuite[0] = cipherSuite0; foundSuite[1] = cipherSuite; } return ret; } /* Attempt to find the PSK (not session ticket) that matches. * * @param [in, out] ssl The SSL/TLS object. * @param [in] psk A pre-shared key from the extension. * @param [out] suite Cipher suite to use with PSK. * @param [out] err Error code. * PSK_KEY_ERROR when key is too big, * UNSUPPORTED_SUITE on invalid suite. * Other error when attempting to derive early secret. * @return 1 when a match found - but check error code. * @return 0 when no match found. */ static int FindPsk(WOLFSSL* ssl, PreSharedKey* psk, const byte* suite, int* err) { int ret = 0; int found = 0; byte foundSuite[SUITE_LEN]; WOLFSSL_ENTER("FindPsk"); XMEMSET(foundSuite, 0, sizeof(foundSuite)); ret = FindPskSuite(ssl, psk, ssl->arrays->psk_key, &ssl->arrays->psk_keySz, suite, &found, foundSuite); if (ret == 0 && found) { /* This identity matched via external PSK callback, not ticket resume. */ 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. */ /* Set PSK ciphersuite into SSL. */ ssl->options.cipherSuite0 = foundSuite[0]; ssl->options.cipherSuite = foundSuite[1]; ret = SetCipherSpecs(ssl); if (ret == 0) { /* Derive the early secret using the PSK. */ ret = DeriveEarlySecret(ssl); } if (ret == 0) { /* PSK negotiation has succeeded */ ssl->options.isPSK = 1; #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK if (!ssl->options.certWithExternPsk) #endif { /* SERVER: using PSK for peer authentication. */ ssl->options.peerAuthGood = 1; } } } *err = ret; WOLFSSL_LEAVE("FindPsk", found); WOLFSSL_LEAVE("FindPsk", ret); return found; } #endif /* !NO_PSK */ /* Handle any Pre-Shared Key (PSK) extension. * Find a PSK that supports the cipher suite passed in. * * ssl SSL/TLS object. * suite Cipher suite to find PSK for. * usingPSK 1=Indicates handshake is using Pre-Shared Keys (2=Ephemeral) * first Set to 1 if first in extension * returns 0 on success and otherwise failure. */ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz, const byte* suite, int* usingPSK, int* first) { int ret = 0; TLSX* ext; PreSharedKey* current; byte binderKey[WC_MAX_DIGEST_SIZE]; byte binder[WC_MAX_DIGEST_SIZE]; word32 binderLen; #if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET) int certWithExternOffered = 0; #endif #ifdef NO_PSK (void) suite; /* to avoid unused var warning when not used */ #endif WOLFSSL_ENTER("DoPreSharedKeys"); (void)suite; #ifdef WOLFSSL_CHECK_MEM_ZERO /* Poison and register binderKey up front; every exit below (including the * error paths) funnels through the cleanup label which zeroes it. */ XMEMSET(binderKey, 0xff, sizeof(binderKey)); wc_MemZero_Add("DoPreSharedKeys binderKey", binderKey, sizeof(binderKey)); #endif #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_EARLY_DATA) ssl->options.ticketPredatesCtx = 0; #ifndef NO_SESSION_CACHE /* Re-derived from this ClientHello's ticket. A stale value from the first * hello of an HRR exchange must not gate 0-RTT for the second. */ ssl->options.ticketCacheHit = 0; #endif #endif ext = TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY); if (ext == NULL) { WOLFSSL_MSG("No pre shared extension keys found"); ret = BAD_FUNC_ARG; goto cleanup; } #if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET) certWithExternOffered = TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) != NULL; #endif /* Look through all client's pre-shared keys for a match. */ for (current = (PreSharedKey*)ext->data; current != NULL; current = current->next) { #ifndef NO_PSK if (current->identityLen > MAX_PSK_ID_LEN) { ret = BUFFER_ERROR; goto cleanup; } XMEMCPY(ssl->arrays->client_identity, current->identity, current->identityLen); ssl->arrays->client_identity[current->identityLen] = '\0'; #endif #ifdef HAVE_SESSION_TICKET /* Decode the identity. */ switch (current->decryptRet) { case PSK_DECRYPT_NONE: ret = DoClientTicket_ex(ssl, current, 1); /* psk->sess may be set. Need to clean up later. */ break; case PSK_DECRYPT_OK: ret = WOLFSSL_TICKET_RET_OK; break; case PSK_DECRYPT_CREATE: ret = WOLFSSL_TICKET_RET_CREATE; break; case PSK_DECRYPT_FAIL: ret = WOLFSSL_TICKET_RET_REJECT; break; } #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) goto cleanup; #endif if (ret != WOLFSSL_TICKET_RET_OK && current->sess_free_cb != NULL) { current->sess_free_cb(ssl, current->sess, ¤t->sess_free_cb_ctx); current->sess = NULL; current->sess_free_cb = NULL; XMEMSET(¤t->sess_free_cb_ctx, 0, sizeof(psk_sess_free_cb_ctx)); } if (ret == WOLFSSL_TICKET_RET_OK) { #if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET) /* RFC 9973 Sect. 5.1: all PSKs listed alongside * tls_cert_with_extern_psk MUST be external PSKs. A successfully * decrypted session ticket identity is a resumption PSK, so the * server MUST abort with illegal_parameter regardless of whether * the ticket would otherwise be acceptable. Check here, before * DoClientTicketFinalize, to avoid polluting ssl->session with * ticket state that will not be used. */ if (certWithExternOffered) { if (current->sess_free_cb != NULL) { current->sess_free_cb(ssl, current->sess, ¤t->sess_free_cb_ctx); current->sess = NULL; current->sess_free_cb = NULL; XMEMSET(¤t->sess_free_cb_ctx, 0, sizeof(psk_sess_free_cb_ctx)); } ret = PSK_KEY_ERROR; WOLFSSL_ERROR_VERBOSE(ret); goto cleanup; } #endif ret = DoClientTicketCheck(ssl, current, ssl->timeout, suite); #if defined(HAVE_SNI) || defined(HAVE_ALPN) if (ret == 0) { /* Decline this PSK if the SNI/ALPN bound to the ticket * does not match the current connection. RFC 6066 Sect. * 3 mandates this for SNI; wolfSSL applies the same * policy to ALPN as defense in depth. Skipping the PSK * (rather than aborting) lets the server try the next * candidate or fall back to a full handshake naturally * without unwinding committed PSK state. ALPN_Select * has already run earlier in DoTls13ClientHello so the * negotiated ALPN is available to TicketAlpnHash. */ byte curHash[TICKET_BINDING_HASH_SZ]; #ifdef HAVE_SNI if (TicketSniHash(ssl, curHash) != 0 || XMEMCMP(curHash, current->it->sniHash, TICKET_BINDING_HASH_SZ) != 0) { WOLFSSL_MSG("Ticket SNI mismatch, skipping PSK"); ret = WOLFSSL_FATAL_ERROR; } #endif #ifdef HAVE_ALPN if (ret == 0 && (TicketAlpnHash(ssl, curHash) != 0 || XMEMCMP(curHash, current->it->alpnHash, TICKET_BINDING_HASH_SZ) != 0)) { WOLFSSL_MSG("Ticket ALPN mismatch, skipping PSK"); ret = WOLFSSL_FATAL_ERROR; } #endif } #endif if (ret == 0) DoClientTicketFinalize(ssl, current->it, current->sess); if (current->sess_free_cb != NULL) { current->sess_free_cb(ssl, current->sess, ¤t->sess_free_cb_ctx); current->sess = NULL; current->sess_free_cb = NULL; XMEMSET(¤t->sess_free_cb_ctx, 0, sizeof(psk_sess_free_cb_ctx)); } if (ret != 0) continue; /* SERVER: using secret in session ticket for peer auth. */ ssl->options.peerAuthGood = 1; #ifdef WOLFSSL_EARLY_DATA ssl->options.maxEarlyDataSz = ssl->session->maxEarlyDataSz; /* RFC 8446 Section 8.2: fresh servers should reject 0-RTT. * Flag tickets minted before this ctx was created. */ if (!ssl->ctx->noFreshStartCheck) { #ifdef WOLFSSL_32BIT_MILLI_TIME /* A 32 bit ms clock wraps every ~49.7 days, so the ctx age is * only exact while it stays below the max ticket age. Past * that point DoClientTicketCheck has already rejected * anything old enough to predate the ctx, so the check can be * skipped. * * ctxAge is unsigned, so a clock reading before the ctx start * time (a backward step) wraps to just under 2^32. Letting * that count as an old ctx would silently disable the check * and admit 0-RTT for tickets minted before a restart, so the * near-wrap band stays in the checked range. * * The two cases are indistinguishable on a wrapping 32 bit * clock, so a ctx aged between (2^32 - max ticket age) and * 2^32 ms also lands in the band and refuses 0-RTT until it * wraps out. Refusing 0-RTT only costs the early data round * trip, so the ambiguity is resolved that way. */ word32 maxAge = (word32)TLS13_MAX_TICKET_AGE * 1000; word32 now = TimeNowInMilliseconds(); word32 ctxAge = now - ssl->ctx->ticketStartTime; word32 delta = ssl->ctx->ticketStartTime - ssl->session->ticketSeen; ssl->options.ticketPredatesCtx = (now != 0 && (ctxAge <= maxAge || ctxAge >= (word32)0u - maxAge) && delta != 0 && delta <= maxAge); #else ssl->options.ticketPredatesCtx = (ssl->session->ticketSeen < ssl->ctx->ticketStartTime); #endif } #endif /* Use the same cipher suite as before and set up for use. */ ssl->options.cipherSuite0 = ssl->session->cipherSuite0; ssl->options.cipherSuite = ssl->session->cipherSuite; ret = SetCipherSpecs(ssl); if (ret != 0) goto cleanup; /* Resumption PSK is resumption master secret. */ ssl->arrays->psk_keySz = ssl->specs.hash_size; if ((ret = DeriveResumptionPSK(ssl, ssl->session->ticketNonce.data, ssl->session->ticketNonce.len, ssl->arrays->psk_key)) != 0) { goto cleanup; } /* The resumed ticket's nonce has derived its PSK. The tickets of * this connection get PSKs from a new resumption master secret, * so their nonces count from 0 again (RFC 8446 Section 4.6.1). */ ssl->session->ticketNonce.len = 0; /* Derive the early secret using the PSK. */ ret = DeriveEarlySecret(ssl); if (ret != 0) goto cleanup; /* Hash data up to binders for deriving binders in PSK extension. * A pended binder derive below re-enters DoTls13ClientHello at * TLS_ASYNC_BEGIN, so the hash is guarded to run only once. */ #ifdef WOLFSSL_ASYNC_CRYPT if (!ssl->options.chHashInput) #endif { ret = HashInput(ssl, input, (int)inputSz); if (ret < 0) goto cleanup; } #ifdef WOLFSSL_ASYNC_CRYPT ssl->options.chHashInput = 1; #endif /* Derive the binder key to use with HMAC. */ ret = DeriveBinderKeyResume(ssl, binderKey); if (ret != 0) goto cleanup; } else #endif /* HAVE_SESSION_TICKET */ #ifndef NO_PSK if (FindPsk(ssl, current, suite, &ret)) { if (ret != 0) goto cleanup; #ifdef WOLFSSL_ASYNC_CRYPT if (!ssl->options.chHashInput) #endif { ret = HashInput(ssl, input, (int)inputSz); if (ret < 0) goto cleanup; } #ifdef WOLFSSL_ASYNC_CRYPT ssl->options.chHashInput = 1; #endif /* Derive the binder key to use with HMAC. */ ret = DeriveBinderKey(ssl, binderKey); if (ret != 0) goto cleanup; } else #endif { continue; } ssl->options.sendVerify = 0; /* Derive the Finished message secret. */ ret = DeriveFinishedSecret(ssl, binderKey, ssl->keys.client_write_MAC_secret, 0 /* neither end */); if (ret != 0) goto cleanup; /* Derive the binder and compare with the one in the extension. */ ret = BuildTls13HandshakeHmac(ssl, ssl->keys.client_write_MAC_secret, binder, &binderLen); if (ret != 0) goto cleanup; if (binderLen != current->binderLen || ConstantCompare(binder, current->binder, binderLen) != 0) { WOLFSSL_ERROR_VERBOSE(BAD_BINDER); ret = BAD_BINDER; goto cleanup; } /* This PSK works, no need to try any more. */ current->chosen = 1; ext->resp = 1; break; } if (current == NULL) { ret = 0; goto cleanup; } *first = (current == ext->data); *usingPSK = 1; cleanup: ForceZero(binderKey, sizeof(binderKey)); ForceZero(binder, sizeof(binder)); #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(binderKey, sizeof(binderKey)); #endif WOLFSSL_LEAVE("DoPreSharedKeys", ret); return ret; } /* Check whether a PSK may be used given the key exchange modes the client * advertised and the modes this server is configured to allow. * * RFC 9846 Section 4.3.9 forbids selecting a mode the client did not list. * Section 4.3.11 says a server that finds no acceptable PSK should perform a * non-PSK handshake instead of aborting. Deciding before a PSK is selected * leaves nothing to unwind: no ticket is decrypted, no binder is verified, no * secret is derived and no early data is accepted. * * The configured policy is read, not ssl->options.noPskDheKe, which also * carries negotiated state and is cleared by every certificate handshake. * * ssl SSL/TLS object. * clSuites Client's cipher suite list. * returns 1 when PSK selection may proceed and 0 when the PSK must be ignored. */ static int PskModesUsable(const WOLFSSL* ssl, const Suites* clSuites) { #ifdef HAVE_SUPPORTED_CURVES TLSX* ext; word32 modes; /* Offering pre_shared_key without psk_key_exchange_modes is a MUST-level * abort (Section 4.3.9). Leave it to CheckPreSharedKeys. */ ext = TLSX_Find(ssl->extensions, TLSX_PSK_KEY_EXCHANGE_MODES); if (ext == NULL) return 1; modes = ext->val; #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK /* RFC 9973 requires psk_dhe_ke and overrides the no-(EC)DHE policy, so a * mismatch must abort rather than fall back. */ if (TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) != NULL) return 1; #endif /* Only decline when a certificate handshake can actually run instead. * These are the same two things DoTls13ClientHello() requires of a * ClientHello that negotiates no PSK. */ if (TLSX_Find(ssl->extensions, TLSX_KEY_SHARE) == NULL) return 1; if (clSuites == NULL || clSuites->hashSigAlgoSz == 0) return 1; if ((modes & (1 << PSK_DHE_KE)) != 0 && !ssl->options.noPskDheKePolicy) return 1; if (ssl->options.onlyPskDheKe) return 0; return (modes & (1 << PSK_KE)) != 0; #else /* Without (EC)DHE there is no certificate handshake to fall back to. */ (void)ssl; (void)clSuites; return 1; #endif } /* Handle any Pre-Shared Key (PSK) extension. * Must do this in ClientHello as it requires a hash of the truncated message. * Don't know size of binders until Pre-Shared Key extension has been parsed. * * ssl SSL/TLS object. * input ClientHello message. * helloSz Size of the ClientHello message (including binders if present). * clSuites Client's cipher suite list. * usingPSK Indicates handshake is using Pre-Shared Keys. */ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, Suites* clSuites, int* usingPSK) { int ret; TLSX* ext; word16 bindersLen; int first = 0; int usePsk; #ifndef WOLFSSL_PSK_ONE_ID int i; const Suites* suites; #else byte suite[2]; #endif WOLFSSL_ENTER("CheckPreSharedKeys"); ext = TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY); if (ext == NULL) { #ifdef WOLFSSL_EARLY_DATA ssl->earlyData = no_early_data; #endif if (usingPSK) *usingPSK = 0; /* No PSK extension at all: if a mandatory external PSK is configured, * refuse the connection rather than continue without one. havePSK is * only set by an external-PSK callback, so a peer relying solely on * session-ticket resumption is unaffected. */ if (ssl->options.havePSK && ssl->options.failNoPSK) { WOLFSSL_ERROR_VERBOSE(PSK_MISSING_ERROR); return PSK_MISSING_ERROR; } /* Hash data up to binders for deriving binders in PSK extension. */ ret = HashInput(ssl, input, (int)helloSz); return ret; } /* Wire-order check that PSK was the last extension in ClientHello is * performed in DoTls13ClientHello immediately after TLSX_Parse, since * post-parse code (e.g. ALPN_Select via TLSX_SetALPN) may legitimately * prepend new entries to ssl->extensions before this point and would * otherwise trip a head-of-list check here. */ /* Assume we are going to resume with a pre-shared key. */ ssl->options.resuming = 1; /* Find the pre-shared key extension and calculate hash of truncated * ClientHello for binders. */ ret = TLSX_PreSharedKey_GetSizeBinders((PreSharedKey*)ext->data, client_hello, &bindersLen); if (ret < 0) return ret; if (bindersLen > helloSz) return BUFFER_ERROR; /* Refine list for PSK processing. */ sslRefineSuites(ssl, clSuites); usePsk = PskModesUsable(ssl, clSuites); if (!usePsk) { WOLFSSL_MSG("No usable psk_key_exchange_modes, ignoring PSK"); } #ifndef WOLFSSL_PSK_ONE_ID if (usingPSK == NULL) return BAD_FUNC_ARG; /* set after refineSuites, to avoid taking a stale ptr to ctx->Suites */ suites = WOLFSSL_SUITES(ssl); /* Server list has only common suites from refining in server or client * order. */ for (i = 0; usePsk && !(*usingPSK) && i < suites->suiteSz; i += 2) { ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen, suites->suites + i, usingPSK, &first); if (ret != 0) { #ifdef HAVE_SESSION_TICKET #ifdef WOLFSSL_ASYNC_CRYPT if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) #endif CleanupClientTickets((PreSharedKey*)ext->data); #endif WOLFSSL_MSG_EX("DoPreSharedKeys: %d", ret); return ret; } } #ifdef HAVE_SESSION_TICKET CleanupClientTickets((PreSharedKey*)ext->data); #endif #else if (usePsk) { ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen, suite, usingPSK, &first); if (ret != 0) { WOLFSSL_MSG_EX("DoPreSharedKeys: %d", ret); return ret; } } #endif if (!*usingPSK) { /* No suitable PSK was negotiated. When a mandatory external PSK is * configured, fail with a dedicated error instead of falling back to a * certificate handshake. This must run before the no-certificate * BAD_BINDER check below so a PSK-only server (no cert) still reports * PSK_MISSING_ERROR. havePSK is only set by an external-PSK callback, so * a peer relying solely on session-ticket resumption is unaffected. */ if (ssl->options.havePSK && ssl->options.failNoPSK) { WOLFSSL_ERROR_VERBOSE(PSK_MISSING_ERROR); return PSK_MISSING_ERROR; } #ifndef NO_CERTS if (ssl->buffers.certificate == NULL #ifdef WOLFSSL_CERT_SETUP_CB && ssl->ctx->certSetupCb == NULL #endif ) #endif { /* Reused for identity protection: an unknown identity must look * like a bad binder, so keep the error code shared. */ WOLFSSL_ERROR_VERBOSE(BAD_BINDER); return BAD_BINDER; } } if (*usingPSK) { /* While verifying the selected PSK, we updated the * handshake hash up to the binder bytes in the PSK extensions. * Continuing, we need the rest of the ClientHello hashed as well. */ ret = HashRaw(ssl, input + helloSz - bindersLen, bindersLen); } else { /* No suitable PSK found, Hash the complete ClientHello, * as caller expect it after we return */ ret = HashInput(ssl, input, (int)helloSz); } if (ret != 0) return ret; if (*usingPSK != 0) { word32 modes; #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK int usingCertWithExternPsk = 0; TLSX* certExt = NULL; TLSX* pskExt = NULL; PreSharedKey* chosenPsk = NULL; #endif #ifdef WOLFSSL_EARLY_DATA TLSX* extEarlyData; #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK int hasCertWithExternPsk = (TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) != NULL); #endif extEarlyData = TLSX_Find(ssl->extensions, TLSX_EARLY_DATA); if (extEarlyData != NULL) { /* Check if accepting early data and first PSK. * RFC 9973: early_data is not compatible with * cert_with_extern_psk, so skip key derivation in that case. */ if (ssl->earlyData != no_early_data && first && ssl->options.maxEarlyDataSz > 0 #ifdef HAVE_SESSION_TICKET /* RFC 8446 section 8.2: freshly started servers should * reject 0-RTT. Tickets minted before this ctx was created * belong to a previous instance. */ && !ssl->options.ticketPredatesCtx #endif #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK && !hasCertWithExternPsk #endif #if defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) /* RFC 8446 section 8: accept 0-RTT once per ticket. Consuming * the cache entry is what bounds the replay, so only accept * when the entry was really evicted. Best effort. */ && ssl->options.ticketCacheHit && wolfSSL_SSL_CTX_remove_session(ssl->ctx, ssl->session) == 1 #endif ) { extEarlyData->resp = 1; #if defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) WOLFSSL_MSG("Accepting early data for a cached ticket session"); #endif /* Derive early data decryption key. */ ret = DeriveTls13Keys(ssl, early_data_key, DECRYPT_SIDE_ONLY, 1); if (ret != 0) return ret; if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0) return ret; ssl->keys.encryptionOn = 1; ssl->earlyData = process_early_data; } else extEarlyData->resp = 0; } #endif /* Get the PSK key exchange modes the client wants to negotiate. */ ext = TLSX_Find(ssl->extensions, TLSX_PSK_KEY_EXCHANGE_MODES); if (ext == NULL) { WOLFSSL_ERROR_VERBOSE(MISSING_HANDSHAKE_DATA); return MISSING_HANDSHAKE_DATA; } modes = ext->val; #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK certExt = TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK); if (certExt != NULL) { pskExt = TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY); if (pskExt != NULL) chosenPsk = (PreSharedKey*)pskExt->data; while (chosenPsk != NULL && !chosenPsk->chosen) chosenPsk = chosenPsk->next; if (chosenPsk == NULL || chosenPsk->resumption) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } if ((modes & (1 << PSK_DHE_KE)) == 0) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } usingCertWithExternPsk = 1; ssl->options.certWithExternPsk = 1; if (clSuites->hashSigAlgoSz == 0) { WOLFSSL_ERROR_VERBOSE(MISSING_HANDSHAKE_DATA); return MISSING_HANDSHAKE_DATA; } ret = PickHashSigAlgo(ssl, clSuites->hashSigAlgo, clSuites->hashSigAlgoSz, 1); if (ret != 0) return ret; ssl->options.sendVerify = SEND_CERT; certExt->resp = 1; #ifdef WOLFSSL_EARLY_DATA /* RFC 9973: early_data is not compatible with * cert_with_extern_psk. TLSX_Parse already rejects the * combination in the ClientHello, but clear the response flag * here as a defense-in-depth measure. */ if (extEarlyData != NULL) { WOLFSSL_MSG("Rejecting early data: " "cert_with_extern_psk is not 0-RTT compatible"); extEarlyData->resp = 0; ssl->earlyData = no_early_data; } #endif } else { ssl->options.certWithExternPsk = 0; } #endif #ifndef HAVE_SUPPORTED_CURVES #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK if (usingCertWithExternPsk) { WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } #endif #endif #ifdef HAVE_SUPPORTED_CURVES ext = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); /* Use (EC)DHE for forward-security if possible. */ if (((modes & (1 << PSK_DHE_KE)) != 0 && !ssl->options.noPskDheKePolicy && ext != NULL) #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK || usingCertWithExternPsk #endif ) { if (ext == NULL) { WOLFSSL_ERROR_VERBOSE(EXT_MISSING); return EXT_MISSING; } /* Resumption path uses previous session group. */ #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK if (!usingCertWithExternPsk) #endif ssl->namedGroup = ssl->session->namedGroup; *usingPSK = 2; /* generate new ephemeral key */ } else if (ssl->options.onlyPskDheKe || (ssl->options.failNoPSK && !ssl->options.resuming)) { /* A mandatory external PSK (failNoPSK) must be combined with * (EC)DHE for forward secrecy, so reject a pure psk_ke * negotiation. Session-ticket resumption is exempt. * onlyPskDheKe only reaches here when PskModesUsable() could not * decline, i.e. there is no certificate handshake to fall back * to. */ WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } else #endif { if ((modes & (1 << PSK_KE)) == 0) { WOLFSSL_MSG("psk_ke mode does not allow key share"); WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); return PSK_KEY_ERROR; } ssl->options.noPskDheKe = 1; ssl->arrays->preMasterSz = 0; *usingPSK = 1; } } else { #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK /* If no PSK is found, we remove the extension to make sure it * is not sent back to the client */ TLSX_Remove(&ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK, ssl->heap); ssl->options.certWithExternPsk = 0; #endif /* No PSK negotiated; the check above already aborted when there is no * certificate. Fall through so the trace is emitted here too. */ } WOLFSSL_LEAVE("CheckPreSharedKeys", ret); return 0; } #endif /* HAVE_SESSION_TICKET || !NO_PSK */ #if defined(WOLFSSL_SEND_HRR_COOKIE) /* 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); 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; } /* Check that the Cookie data's integrity. * * ssl SSL/TLS object. * cookie The cookie data - hash and MAC. * 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) { int ret; byte cookieType = 0; byte macSz = 0; #ifndef NO_SHA256 cookieType = WC_SHA256; macSz = WC_SHA256_DIGEST_SIZE; #elif defined(WOLFSSL_SHA384) cookieType = WC_SHA384; macSz = WC_SHA384_DIGEST_SIZE; #elif defined(WOLFSSL_TLS13_SHA512) cookieType = WC_SHA512; macSz = WC_SHA512_DIGEST_SIZE; #elif defined(WOLFSSL_SM3) cookieType = WC_SM3; macSz = WC_SM3_DIGEST_SIZE; #else #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) #ifdef WOLFSSL_DTLS13 && (ssl->buffers.tls13CookieSecretSecondary.buffer == NULL || ssl->buffers.tls13CookieSecretSecondary.length == 0) #endif ) { WOLFSSL_MSG("Missing DTLS 1.3 cookie secret"); return COOKIE_ERROR; } if (cookieSz < ssl->specs.hash_size + macSz) return HRR_COOKIE_ERROR; cookieSz -= macSz; /* 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. The secondary secret is * DTLS 1.3 only, so its verify path is compiled in only for WOLFSSL_DTLS13. */ ret = WC_NO_ERR_TRACE(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 != WC_NO_ERR_TRACE(HRR_COOKIE_ERROR)) return ret; } #ifdef WOLFSSL_DTLS13 if (ret == WC_NO_ERR_TRACE(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 != WC_NO_ERR_TRACE(HRR_COOKIE_ERROR)) return ret; } #endif if (ret != 0) { WOLFSSL_ERROR_VERBOSE(HRR_COOKIE_ERROR); return HRR_COOKIE_ERROR; } return cookieSz; } /* Length of the KeyShare Extension */ #define HRR_KEY_SHARE_SZ (OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN) /* Length of the Supported Versions Extension */ #define HRR_VERSIONS_SZ (OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN) /* Length of the Cookie Extension excluding cookie data */ #define HRR_COOKIE_HDR_SZ (OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN) /* PV | Random | Session Id | CipherSuite | Compression | Ext Len */ #define HRR_BODY_SZ (VERSION_SZ + RAN_LEN + ENUM_LEN + ID_LEN + \ SUITE_LEN + COMP_LEN + OPAQUE16_LEN) /* HH | PV | CipherSuite | Ext Len | Key Share | Supported Version | Cookie */ #define MAX_HRR_SZ (HRR_MAX_HS_HEADER_SZ + \ HRR_BODY_SZ + \ HRR_KEY_SHARE_SZ + \ HRR_VERSIONS_SZ + \ HRR_COOKIE_HDR_SZ) /* Restart the handshake hash from the cookie value. * * ssl SSL/TLS object. * cookie Cookie data from client. * returns 0 on success, otherwise failure. */ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie) { byte header[HANDSHAKE_HEADER_SZ] = {0}; byte hrr[MAX_HRR_SZ] = {0}; int hrrIdx; word32 idx; byte hashSz; byte* cookieData; word16 cookieDataSz; word16 length; int keyShareExt = 0; int ret; byte sessIdSz; ret = TlsCheckCookie(ssl, cookie->data, cookie->len); if (ret < 0) return ret; cookieDataSz = (word16)ret; hashSz = cookie->data[0]; cookieData = cookie->data; idx = OPAQUE8_LEN; /* Restart handshake hash with synthetic message hash. */ AddTls13HandShakeHeader(header, hashSz, 0, 0, message_hash, ssl); if ((ret = InitHandshakeHashes(ssl)) != 0) return ret; if ((ret = HashRaw(ssl, header, sizeof(header))) != 0) return ret; #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Restart Hash from Cookie"); WOLFSSL_BUFFER(cookieData + idx, hashSz); #endif if ((ret = HashRaw(ssl, cookieData + idx, hashSz)) != 0) return ret; /* Reconstruct the HelloRetryMessage for handshake hash. */ sessIdSz = ssl->session->sessionIDSz; #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID) /* RFC 9147 Section 5: a DTLS 1.3 server does not echo the session ID, so * the reconstructed transcript must not carry one either. */ if (ssl->options.dtls) sessIdSz = 0; #endif length = HRR_BODY_SZ - ID_LEN + sessIdSz + HRR_COOKIE_HDR_SZ + cookie->len; length += HRR_VERSIONS_SZ; /* HashSz (1 byte) + Hash (HashSz bytes) + CipherSuite (2 bytes) */ if (cookieDataSz > OPAQUE8_LEN + hashSz + OPAQUE16_LEN) { keyShareExt = 1; length += HRR_KEY_SHARE_SZ; } AddTls13HandShakeHeader(hrr, length, 0, 0, server_hello, ssl); idx += hashSz; hrrIdx = HANDSHAKE_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) hrrIdx += DTLS_HANDSHAKE_EXTRA; #endif /* WOLFSSL_DTLS13 */ /* The negotiated protocol version. */ hrr[hrrIdx++] = ssl->version.major; hrr[hrrIdx++] = ssl->options.dtls ? DTLSv1_2_MINOR : TLSv1_2_MINOR; /* HelloRetryRequest message has fixed value for random. */ XMEMCPY(hrr + hrrIdx, helloRetryRequestRandom, RAN_LEN); hrrIdx += RAN_LEN; hrr[hrrIdx++] = sessIdSz; if (sessIdSz > 0) { XMEMCPY(hrr + hrrIdx, ssl->session->sessionID, sessIdSz); hrrIdx += sessIdSz; } /* Restore the cipher suite from the cookie. */ ssl->options.hrrCipherSuite0 = cookieData[idx]; hrr[hrrIdx++] = cookieData[idx++]; ssl->options.hrrCipherSuite = cookieData[idx]; hrr[hrrIdx++] = cookieData[idx++]; /* Compression not supported in TLS v1.3. */ hrr[hrrIdx++] = 0; /* Extensions' length */ length -= HRR_BODY_SZ - ID_LEN + sessIdSz; c16toa(length, hrr + hrrIdx); hrrIdx += 2; /* Optional KeyShare Extension */ if (keyShareExt) { c16toa(TLSX_KEY_SHARE, hrr + hrrIdx); hrrIdx += 2; c16toa(OPAQUE16_LEN, hrr + hrrIdx); hrrIdx += 2; /* Restore the HRR key share group from the cookie. */ ato16(cookieData + idx, &ssl->hrr_keyshare_group); hrr[hrrIdx++] = cookieData[idx++]; hrr[hrrIdx++] = cookieData[idx++]; } c16toa(TLSX_SUPPORTED_VERSIONS, hrr + hrrIdx); hrrIdx += 2; c16toa(OPAQUE16_LEN, hrr + hrrIdx); hrrIdx += 2; #ifdef WOLFSSL_TLS13_DRAFT hrr[hrrIdx++] = TLS_DRAFT_MAJOR; hrr[hrrIdx++] = TLS_DRAFT_MINOR; #else hrr[hrrIdx++] = ssl->version.major; hrr[hrrIdx++] = ssl->version.minor; #endif /* Mandatory Cookie Extension */ c16toa(TLSX_COOKIE, hrr + hrrIdx); hrrIdx += 2; c16toa(cookie->len + OPAQUE16_LEN, hrr + hrrIdx); hrrIdx += 2; c16toa(cookie->len, hrr + hrrIdx); hrrIdx += 2; #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Reconstructed HelloRetryRequest"); WOLFSSL_BUFFER(hrr, hrrIdx); WOLFSSL_MSG("Cookie"); WOLFSSL_BUFFER(cookieData, cookie->len); #endif #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Dtls13HashHandshake(ssl, hrr, (word16)hrrIdx); } else #endif /* WOLFSSL_DTLS13 */ { ret = HashRaw(ssl, hrr, hrrIdx); } if (ret != 0) return ret; return HashRaw(ssl, cookieData, cookie->len); } #endif /* Do SupportedVersion extension for TLS v1.3+ otherwise it is not. * * ssl The SSL/TLS object. * input The message buffer. * i The index into the message buffer of ClientHello. * helloSz The length of the current handshake message. * returns 0 on success and otherwise failure. */ static int DoTls13SupportedVersions(WOLFSSL* ssl, const byte* input, word32 i, word32 helloSz, int* wantDowngrade) { int ret; byte b; word16 suiteSz; word16 totalExtSz; int foundVersion = 0; /* Client random */ i += RAN_LEN; if (i > helloSz) return BUFFER_ERROR; /* Session id - not used in TLS v1.3 */ if (helloSz - i < OPAQUE8_LEN) { return BUFFER_ERROR; } b = input[i++]; if (b > helloSz - i) { return BUFFER_ERROR; } i += b; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { /* legacy_cookie - not used in DTLS v1.3 */ if (helloSz - i < OPAQUE8_LEN) { return BUFFER_ERROR; } b = input[i++]; if (b > helloSz - i) { return BUFFER_ERROR; } i += b; } #endif /* WOLFSSL_DTLS13 */ /* Cipher suites */ if (helloSz - i < OPAQUE16_LEN) return BUFFER_ERROR; ato16(input + i, &suiteSz); i += OPAQUE16_LEN; if ((word32)suiteSz + OPAQUE8_LEN > helloSz - i) return BUFFER_ERROR; i += suiteSz; /* Compression */ b = input[i++]; if (b > helloSz - i) return BUFFER_ERROR; i += b; /* TLS 1.3 must have extensions */ if (i < helloSz) { if (helloSz - i < OPAQUE16_LEN) return BUFFER_ERROR; ato16(&input[i], &totalExtSz); i += OPAQUE16_LEN; if (totalExtSz != helloSz - i) return BUFFER_ERROR; /* Need to negotiate version first. */ if ((ret = TLSX_ParseVersion(ssl, input + i, totalExtSz, client_hello, &foundVersion))) { return ret; } } *wantDowngrade = !foundVersion || !IsAtLeastTLSv1_3(ssl->version); return 0; } #ifdef HAVE_ECH /* Calculate and write the 8 ECH confirmation bytes. * Output into confirmation field on HRR and into ServerRandom on ServerHello. * * ssl SSL/TLS object. * label Ascii string describing ECH acceptance or rejection. * labelSz Length of label excluding NULL character. * output The buffer to calculate/write confirmation from/to. * acceptOffset Where the 8 ECH confirmation bytes should be placed. * helloSz Size of hello message. * msgType Type of message being written. * returns 0 on success and otherwise failure. */ static int EchWriteAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, byte* output, int acceptOffset, int helloSz, byte msgType) { int ret = 0; int headerSz; HS_Hashes* tmpHashes; #ifdef WOLFSSL_DTLS13 headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ : HANDSHAKE_HEADER_SZ; #else headerSz = HANDSHAKE_HEADER_SZ; #endif ret = EchCalcAcceptance(ssl, label, labelSz, output, acceptOffset, helloSz - headerSz, msgType == hello_retry_request, output + acceptOffset); if (ret == 0) { tmpHashes = ssl->hsHashes; ssl->hsHashes = ssl->hsHashesEch; /* after HRR, hsHashesEch must contain: * message_hash(ClientHelloInner1) || HRR (actual, not zeros) */ if (msgType == hello_retry_request) { ret = HashRaw(ssl, output, helloSz); } /* normal TLS code will calculate transcript of ServerHello */ else { ssl->hsHashes = tmpHashes; FreeHandshakeHashes(ssl); tmpHashes = ssl->hsHashesEch; ssl->hsHashesEch = NULL; } ssl->hsHashes = tmpHashes; } return ret; } #endif /* Handle a ClientHello handshake message. * If the protocol version in the message is not TLS v1.3 or higher, use * DoClientHello() * Only a server will receive this message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of ClientHello. * On exit, the index of byte after the ClientHello message and * padding. * helloSz The length of the current handshake message. * returns 0 on success and otherwise failure. */ typedef struct Dch13Args { ProtocolVersion pv; word32 idx; word32 begin; int usingPSK; } Dch13Args; static void FreeDch13Args(WOLFSSL* ssl, void* pArgs) { /* openssl compat builds hang on to the client suites until WOLFSSL object * is destroyed */ #ifndef OPENSSL_EXTRA if (ssl->clSuites) { XFREE(ssl->clSuites, ssl->heap, DYNAMIC_TYPE_SUITES); ssl->clSuites = NULL; } #endif (void)ssl; (void)pArgs; } int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 helloSz) { int ret; #ifdef WOLFSSL_ASYNC_CRYPT Dch13Args* args = NULL; WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args); #else Dch13Args args[1]; #endif #if defined(HAVE_ECH) TLSX* echX = NULL; #endif WOLFSSL_START(WC_FUNC_CLIENT_HELLO_DO); WOLFSSL_ENTER("DoTls13ClientHello"); #ifdef WOLFSSL_ASYNC_CRYPT if (ssl->async == NULL) { ssl->async = (struct WOLFSSL_ASYNC*) XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap, DYNAMIC_TYPE_ASYNC); if (ssl->async == NULL) ERROR_OUT(MEMORY_E, exit_dch); /* Zeroed so the mid-flight resume test below can never route into * uninitialised args. */ XMEMSET(ssl->async, 0, sizeof(struct WOLFSSL_ASYNC)); } args = (Dch13Args*)ssl->async->args; ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* Check for error */ if (ret < 0) { goto exit_dch; } } else if (ssl->options.asyncState > TLS_ASYNC_BEGIN && ssl->options.asyncState < TLS_ASYNC_END) { /* Mid-flight replay: the event may already be retired but * asyncState/args are intact, so resume; resetting would hash the * message twice. Fresh ClientHellos arrive at TLS_ASYNC_BEGIN. */ ret = 0; } else #endif { /* Reset state */ ret = WC_NO_ERR_TRACE(VERSION_ERROR); ssl->options.asyncState = TLS_ASYNC_BEGIN; XMEMSET(args, 0, sizeof(Dch13Args)); #ifdef WOLFSSL_ASYNC_CRYPT ssl->async->freeArgs = FreeDch13Args; #endif } switch (ssl->options.asyncState) { case TLS_ASYNC_BEGIN: { byte b; byte sessIdSz; int wantDowngrade = 0; word16 totalExtSz = 0; #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) /* Reset for each ClientHello, including retries and legacy fallbacks. */ ssl->options.haveSupportedVersions = 0; #endif #ifdef WOLFSSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello"); if (ssl->toInfoOn) AddLateName("ClientHello", &ssl->timeoutInfo); #endif /* do not change state in the SSL object before the next region of code * to be able to statelessly compute a DTLS cookie */ #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE) /* No-cookie accepts are already stateful before the first read. */ if (IsDtlsNotSctpMode(ssl) && ssl->options.sendCookie && !ssl->options.dtlsStateful) { DtlsSetSeqNumForReply(ssl); ret = DoClientHelloStateless(ssl, input + *inOutIdx, helloSz, 0, NULL); if (ret != 0 || !ssl->options.dtlsStateful) { *inOutIdx += helloSz; goto exit_dch; } if (ssl->chGoodCb != NULL) { int cbret = ssl->chGoodCb(ssl, ssl->chGoodCtx); if (cbret < 0) { ssl->error = cbret; WOLFSSL_MSG("ClientHello Good Cb don't continue error"); return WOLFSSL_FATAL_ERROR; } } } ssl->options.dtlsStateful = 1; #endif /* WOLFSSL_DTLS */ args->idx = *inOutIdx; args->begin = args->idx; /* protocol version, random and session id length check */ if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz) { ERROR_OUT(BUFFER_ERROR, exit_dch); } /* Protocol version */ XMEMCPY(&args->pv, input + args->idx, OPAQUE16_LEN); ssl->chVersion = args->pv; /* store */ args->idx += OPAQUE16_LEN; /* this check pass for DTLS Major (0xff) */ if (args->pv.major < SSLv3_MAJOR) { WOLFSSL_MSG("Legacy version field contains unsupported value"); ERROR_OUT(VERSION_ERROR, exit_dch); } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && args->pv.major == DTLS_MAJOR && args->pv.minor > DTLSv1_2_MINOR) { wantDowngrade = 1; ssl->version.minor = args->pv.minor; } #endif /* WOLFSSL_DTLS13 */ if (!ssl->options.dtls) { #ifndef WOLFSSL_ALLOW_BAD_TLS_LEGACY_VERSION /* Check for TLS 1.3 version (0x0304) in legacy version field. RFC 8446 * Section 4.2.1 allows this action: * * "Servers MAY abort the handshake upon receiving a ClientHello with * legacy_version 0x0304 or later." * * Note that if WOLFSSL_ALLOW_BAD_TLS_LEGACY_VERSION is defined then the * semantics of RFC 5246 Appendix E will be followed. A ServerHello with * version 1.2 will be sent. The same is true if TLS 1.3 is not enabled. */ if (args->pv.major == SSLv3_MAJOR && args->pv.minor >= TLSv1_3_MINOR) { WOLFSSL_MSG("Legacy version field is TLS 1.3 or later. Aborting."); ERROR_OUT(VERSION_ERROR, exit_dch); } #endif /* WOLFSSL_ALLOW_BAD_TLS_LEGACY_VERSION */ /* Legacy protocol version cannot negotiate TLS 1.3 or higher. */ if (args->pv.major > SSLv3_MAJOR || (args->pv.major == SSLv3_MAJOR && args->pv.minor >= TLSv1_3_MINOR)) { args->pv.major = SSLv3_MAJOR; args->pv.minor = TLSv1_2_MINOR; wantDowngrade = 1; ssl->version.minor = args->pv.minor; } /* Legacy version must be [ SSLv3_MAJOR, TLSv1_2_MINOR ] for TLS v1.3 */ else if (args->pv.major == SSLv3_MAJOR && args->pv.minor < TLSv1_2_MINOR) { wantDowngrade = 1; ssl->version.minor = args->pv.minor; } } if (!wantDowngrade) { ret = DoTls13SupportedVersions(ssl, input + args->begin, args->idx - args->begin, helloSz, &wantDowngrade); if (ret < 0) goto exit_dch; } if (wantDowngrade) { #ifndef WOLFSSL_NO_TLS12 byte realMinor; #endif #if defined(HAVE_ECH) if (ssl->options.echProcessingInner) { WOLFSSL_MSG("ECH: inner client hello does not support version " "less than TLS v1.3"); ERROR_OUT(INVALID_PARAMETER, exit_dch); } #endif #ifndef WOLFSSL_NO_TLS12 if (!ssl->options.downgrade) { WOLFSSL_MSG("Client trying to connect with lesser version than " "TLS v1.3"); ERROR_OUT(VERSION_ERROR, exit_dch); } if ((!ssl->options.dtls && args->pv.minor < ssl->options.minDowngrade) || (ssl->options.dtls && args->pv.minor > ssl->options.minDowngrade)) { WOLFSSL_MSG("\tversion below minimum allowed, fatal error"); ERROR_OUT(VERSION_ERROR, exit_dch); } realMinor = ssl->version.minor; ssl->version.minor = args->pv.minor; ret = HashInput(ssl, input + args->begin, (int)helloSz); ssl->version.minor = realMinor; if (ret == 0) { ret = DoClientHello(ssl, input, inOutIdx, helloSz); } goto exit_dch; #else WOLFSSL_MSG("Client trying to connect with lesser version than " "TLS v1.3"); ERROR_OUT(VERSION_ERROR, exit_dch); #endif } /* From here on we are a TLS 1.3 ClientHello. */ /* Client random * ECH Accepted -> This will fill with the innerClientRandom */ XMEMCPY(ssl->arrays->clientRandom, input + args->idx, RAN_LEN); args->idx += RAN_LEN; #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("client random"); WOLFSSL_BUFFER(ssl->arrays->clientRandom, RAN_LEN); #endif sessIdSz = input[args->idx++]; if (sessIdSz > ID_LEN) { ERROR_OUT(INVALID_PARAMETER, exit_dch); } if (sessIdSz + args->idx > helloSz) ERROR_OUT(BUFFER_ERROR, exit_dch); #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID) /* RFC 9147 Section 5: "DTLS servers MUST NOT echo the legacy_session_id * value from the client." Don't store it so SendTls13ServerHello can't * echo it. */ if (ssl->options.dtls) { ssl->session->sessionIDSz = 0; } else #endif { ssl->session->sessionIDSz = sessIdSz; if (sessIdSz > 0) XMEMCPY(ssl->session->sessionID, input + args->idx, sessIdSz); } args->idx += sessIdSz; /* RFC 8446 Appendix D.4: server MUST only send CCS if the client's * ClientHello contains a non-empty legacy_session_id. An ECH inner hello * is rebuilt with the outer session id, so it decides either way. */ if (sessIdSz == 0) { ssl->options.tls13MiddleBoxCompat = 0; } #ifdef WOLFSSL_QUIC /* RFC 9001 Section 8.4: QUIC has no compatibility mode to be had. */ if (WOLFSSL_IS_QUIC(ssl)) { ssl->options.tls13MiddleBoxCompat = 0; } #endif #ifdef WOLFSSL_DTLS13 /* legacy_cookie */ if (ssl->options.dtls) { word32 rel = args->idx - args->begin; byte cookieLen; if (rel > helloSz || helloSz - rel < OPAQUE8_LEN) ERROR_OUT(BUFFER_ERROR, exit_dch); /* https://www.rfc-editor.org/rfc/rfc9147.html#section-5.3 */ cookieLen = input[args->idx++]; if (cookieLen != 0) { ERROR_OUT(INVALID_PARAMETER, exit_dch); } } #endif /* WOLFSSL_DTLS13 */ XFREE(ssl->clSuites, ssl->heap, DYNAMIC_TYPE_SUITES); ssl->clSuites = (Suites*)XMALLOC(sizeof(Suites), ssl->heap, DYNAMIC_TYPE_SUITES); if (ssl->clSuites == NULL) { ERROR_OUT(MEMORY_E, exit_dch); } /* Cipher suites */ if ((args->idx - args->begin) + OPAQUE16_LEN > helloSz) ERROR_OUT(BUFFER_ERROR, exit_dch); ato16(&input[args->idx], &ssl->clSuites->suiteSz); args->idx += OPAQUE16_LEN; if ((ssl->clSuites->suiteSz % 2) != 0) { ERROR_OUT(INVALID_PARAMETER, exit_dch); } /* suites and compression length check */ if ((args->idx - args->begin) + ssl->clSuites->suiteSz + OPAQUE8_LEN > helloSz) { ERROR_OUT(BUFFER_ERROR, exit_dch); } if (ssl->clSuites->suiteSz > WOLFSSL_MAX_SUITE_SZ) ERROR_OUT(BUFFER_ERROR, exit_dch); XMEMCPY(ssl->clSuites->suites, input + args->idx, ssl->clSuites->suiteSz); args->idx += ssl->clSuites->suiteSz; ssl->clSuites->hashSigAlgoSz = 0; #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) /* Discard any list kept from a previous ClientHello on this object; a * second hello that drops signature_algorithms_cert must not inherit it. */ XFREE(ssl->certHashSigAlgo, ssl->heap, DYNAMIC_TYPE_TLSX); ssl->certHashSigAlgo = NULL; ssl->certHashSigAlgoSz = 0; #endif /* Compression */ b = input[args->idx++]; if ((args->idx - args->begin) + b > helloSz) ERROR_OUT(BUFFER_ERROR, exit_dch); if (b != COMP_LEN) { WOLFSSL_MSG("Must be one compression type in list"); ERROR_OUT(INVALID_PARAMETER, exit_dch); } b = input[args->idx++]; if (b != NO_COMPRESSION) { WOLFSSL_MSG("Must be no compression type in list"); ERROR_OUT(INVALID_PARAMETER, exit_dch); } /* Extensions */ if ((args->idx - args->begin) == helloSz) ERROR_OUT(BUFFER_ERROR, exit_dch); if ((args->idx - args->begin) + OPAQUE16_LEN > helloSz) ERROR_OUT(BUFFER_ERROR, exit_dch); ato16(&input[args->idx], &totalExtSz); args->idx += OPAQUE16_LEN; if ((args->idx - args->begin) + totalExtSz > helloSz) ERROR_OUT(BUFFER_ERROR, exit_dch); /* Auto populate extensions supported unless user defined. */ if ((ret = TLSX_PopulateExtensions(ssl, 1)) != 0) goto exit_dch; #if defined(HAVE_ECH) if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { /* save the start of the buffer so we can use it when parsing ech */ echX = TLSX_Find(ssl->extensions, TLSX_ECH); if (echX == NULL) ERROR_OUT(WOLFSSL_FATAL_ERROR, exit_dch); ((WOLFSSL_ECH*)echX->data)->aad = input + args->begin; ((WOLFSSL_ECH*)echX->data)->aadLen = helloSz; } #endif /* Parse extensions */ if ((ret = TLSX_Parse(ssl, input + args->idx, totalExtSz, client_hello, ssl->clSuites))) { goto exit_dch; } #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) SetPeerSha1CertOk(ssl, ssl->clSuites); #endif #if (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \ defined(HAVE_TLS_EXTENSIONS) /* RFC 8446 Section 4.2.11: the pre_shared_key extension MUST be the * last extension in the ClientHello. wolfSSL stores extensions in * reverse wire order (TLSX_Push prepends), so a well-formed * ClientHello with PSK leaves PSK at the head of ssl->extensions * here, before any post-parse code (e.g. ALPN_Select) modifies the * list. */ { TLSX* pskExt = TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY); if (pskExt != NULL && ssl->extensions != pskExt) { WOLFSSL_MSG("pre_shared_key extension was not last in " "ClientHello"); WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); ERROR_OUT(PSK_KEY_ERROR, exit_dch); } } #endif #if defined(HAVE_ECH) /* ECH accept/reject reconciliation is done at the end of TLSX_Parse. On * acceptance the inner hello was decrypted, so jump to exit and let the * caller re-invoke with the inner hello. */ if (!ssl->options.echProcessingInner && echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE && ((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { goto exit_dch; } #endif #ifdef HAVE_SNI if ((ret = SNI_Callback(ssl)) != 0) goto exit_dch; ssl->options.side = WOLFSSL_SERVER_END; #endif args->idx += totalExtSz; ssl->options.haveSessionId = 1; ssl->options.sendVerify = SEND_CERT; #if defined(WOLFSSL_SEND_HRR_COOKIE) ssl->options.cookieGood = 0; if (ssl->options.sendCookie && (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE #ifdef WOLFSSL_DTLS13 /* Always check for a valid cookie since we may have already * sent a HRR but we reset the state. */ || ssl->options.dtls #endif )) { TLSX* ext = TLSX_Find(ssl->extensions, TLSX_COOKIE); if (ext != NULL) { /* Ensure the cookie came from client and isn't the one in the * response - HelloRetryRequest. */ if (ext->resp == 0) { ret = RestartHandshakeHashWithCookie(ssl, (Cookie*)ext->data); if (ret != 0) goto exit_dch; /* Don't change state here as we may want to enter * DoTls13ClientHello again. */ ssl->options.cookieGood = 1; } else { ERROR_OUT(HRR_COOKIE_ERROR, exit_dch); } } else { #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS13_NO_HRR_ON_RESUME) /* Don't error out as we may be resuming. We confirm this later. */ if (!ssl->options.dtls) #endif ERROR_OUT(HRR_COOKIE_ERROR, exit_dch); } } #endif #ifdef HAVE_SUPPORTED_CURVES if (ssl->hrr_keyshare_group != 0) { /* * https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8 * when sending the new ClientHello, the client MUST * replace the original "key_share" extension with one containing only * a new KeyShareEntry for the group indicated in the selected_group * field of the triggering HelloRetryRequest. */ TLSX* extension = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); if (extension != NULL) { KeyShareEntry* kse = (KeyShareEntry*)extension->data; /* Exactly one KeyShareEntry with the HRR group must be present. */ if (kse == NULL || kse->next != NULL || kse->group != ssl->hrr_keyshare_group) { ERROR_OUT(BAD_KEY_SHARE_DATA, exit_dch); } } else ERROR_OUT(BAD_KEY_SHARE_DATA, exit_dch); } #endif #if defined(HAVE_ECH) /* hash clientHelloInner to hsHashesEch */ if (echX != NULL && ssl->ctx->echConfigs != NULL && !ssl->options.disableECH && ((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { ret = EchHashHelloInner(ssl, (WOLFSSL_ECH*)echX->data); if (ret != 0) goto exit_dch; ((WOLFSSL_ECH*)echX->data)->innerCount = 1; } #endif #ifdef HAVE_ALPN /* Select the ALPN protocol before PSK selection so that the * selected value is available to the per-PSK SNI/ALPN binding check * inside CheckPreSharedKeys/DoPreSharedKeys. ALPN_Select itself * only inspects ssl->extensions and the app callback; it does not * depend on any state set during PSK validation. */ if ((ret = ALPN_Select(ssl)) != 0) goto exit_dch; #endif #if (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \ defined(HAVE_TLS_EXTENSIONS) ret = CheckPreSharedKeys(ssl, input + args->begin, helloSz, ssl->clSuites, &args->usingPSK); if (ret != 0) goto exit_dch; #else if ((ret = HashInput(ssl, input + args->begin, (int)helloSz)) != 0) goto exit_dch; #endif #if (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \ defined(HAVE_TLS_EXTENSIONS) if (!args->usingPSK #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK || ssl->options.certWithExternPsk #endif ) #endif { #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* Not using PSK so don't require no KE. */ ssl->options.noPskDheKe = 0; #endif #ifndef NO_CERTS if (TLSX_Find(ssl->extensions, TLSX_KEY_SHARE) == NULL) { WOLFSSL_MSG("Client did not send a KeyShare extension"); ERROR_OUT(INCOMPLETE_DATA, exit_dch); } /* Can't check ssl->extensions here as SigAlgs are unconditionally set by TLSX_PopulateExtensions */ if (ssl->clSuites->hashSigAlgoSz == 0) { WOLFSSL_MSG("Client did not send a SignatureAlgorithms extension"); ERROR_OUT(INCOMPLETE_DATA, exit_dch); } #else ERROR_OUT(INVALID_PARAMETER, exit_dch); #endif } } /* case TLS_ASYNC_BEGIN */ FALL_THROUGH; case TLS_ASYNC_BUILD: /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_DO; FALL_THROUGH; case TLS_ASYNC_DO: { #ifdef WOLFSSL_CERT_SETUP_CB if ((ret = CertSetupCbWrapper(ssl)) != 0) goto exit_dch; #endif #ifndef NO_CERTS if (!args->usingPSK) { if ((ret = MatchSuite(ssl, ssl->clSuites)) < 0) { #ifdef WOLFSSL_ASYNC_CRYPT if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) #endif WOLFSSL_MSG("Unsupported cipher suite, ClientHello 1.3"); goto exit_dch; } } #endif #ifdef HAVE_SUPPORTED_CURVES if (args->usingPSK == 2) { /* Pick key share and Generate a new key if not present. */ int doHelloRetry = 0; ret = TLSX_KeyShare_Establish(ssl, &doHelloRetry); if (doHelloRetry) { /* Make sure we don't send HRR twice */ if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) ERROR_OUT(INVALID_PARAMETER, exit_dch); ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE; if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ret = 0; /* for hello_retry return 0 */ } if (ret != 0) goto exit_dch; } #endif /* Verify the cipher suite is the same as what was chosen in HRR. * got_client_hello == 2 covers the stateful path. * cookieGood covers the stateless DTLS path. */ if ((ssl->msgsReceived.got_client_hello == 2 #ifdef WOLFSSL_SEND_HRR_COOKIE || ssl->options.cookieGood #endif ) && (ssl->options.cipherSuite0 != ssl->options.hrrCipherSuite0 || ssl->options.cipherSuite != ssl->options.hrrCipherSuite)) { WOLFSSL_MSG("Cipher suite in second ClientHello does not match " "HelloRetryRequest"); ERROR_OUT(INVALID_PARAMETER, exit_dch); } /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_VERIFY; } /* case TLS_ASYNC_BUILD || TLS_ASYNC_DO */ FALL_THROUGH; case TLS_ASYNC_VERIFY: { #if defined(WOLFSSL_ASYNC_CRYPT) && defined(HAVE_SUPPORTED_CURVES) /* Check if the KeyShare calculations from the previous state are complete. * wolfSSL_AsyncPop advances ssl->options.asyncState so we may end up here * with a pending calculation. */ TLSX* extension = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); if (extension != NULL && extension->resp == 1) { KeyShareEntry* serverKSE = (KeyShareEntry*)extension->data; if (serverKSE != NULL && serverKSE->lastRet == WC_NO_ERR_TRACE(WC_PENDING_E)) { #if defined(WOLFSSL_HAVE_MLKEM) if (WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(serverKSE->group)) { ret = TLSX_KeyShare_HandlePqcHybridKeyServer(ssl, serverKSE, serverKSE->ke, serverKSE->keLen); } else #endif { ret = TLSX_KeyShare_GenKey(ssl, serverKSE); } if (ret != 0) goto exit_dch; } } #endif /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_FINALIZE; } FALL_THROUGH; case TLS_ASYNC_FINALIZE: { *inOutIdx = args->idx; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ssl->options.pskNegotiated = (args->usingPSK != 0); #endif if (!args->usingPSK) { #ifndef NO_CERTS /* Check that the negotiated ciphersuite matches protocol version. */ #ifdef HAVE_NULL_CIPHER if (ssl->options.cipherSuite0 == ECC_BYTE && (ssl->options.cipherSuite == TLS_SHA256_SHA256 || ssl->options.cipherSuite == TLS_SHA384_SHA384)) { ; } else #endif #if defined(WOLFSSL_SM4_GCM) && defined(WOLFSSL_SM3) if (ssl->options.cipherSuite0 == CIPHER_BYTE && ssl->options.cipherSuite == TLS_SM4_GCM_SM3) { ; /* Do nothing. */ } else #endif #if defined(WOLFSSL_SM4_CCM) && defined(WOLFSSL_SM3) if (ssl->options.cipherSuite0 == CIPHER_BYTE && ssl->options.cipherSuite == TLS_SM4_CCM_SM3) { ; /* Do nothing. */ } else #endif if (ssl->options.cipherSuite0 != TLS13_BYTE) { WOLFSSL_MSG("Negotiated ciphersuite from lesser version than " "TLS v1.3"); ERROR_OUT(MATCH_SUITE_ERROR, exit_dch); } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) if (ssl->options.resuming) { ssl->options.resuming = 0; ssl->arrays->psk_keySz = 0; XMEMSET(ssl->arrays->psk_key, 0, ssl->specs.hash_size); } #endif /* Derive early secret for handshake secret. */ if ((ret = DeriveEarlySecret(ssl)) != 0) goto exit_dch; #endif /* !NO_CERTS */ } /* Advanced only after the derive: earlier would let the accept loop * proceed on an unfinished early secret. */ ssl->options.clientState = CLIENT_HELLO_COMPLETE; break; } /* case TLS_ASYNC_FINALIZE */ default: ret = INPUT_CASE_ERROR; } /* switch (ssl->options.asyncState) */ #ifdef WOLFSSL_SEND_HRR_COOKIE if (ret == 0 && ssl->options.sendCookie) { if (ssl->options.cookieGood && ssl->options.acceptState == TLS13_ACCEPT_FIRST_REPLY_DONE) { /* Processing second ClientHello. Clear HRR state. */ ssl->options.serverState = NULL_STATE; } if (ssl->options.cookieGood && ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) { /* If we already verified the peer with a cookie then we can't * do another HRR for cipher negotiation. Send alert and restart * the entire handshake. */ ERROR_OUT(INVALID_PARAMETER, exit_dch); } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) { /* Cookie and key share negotiation should be handled in * DoClientHelloStateless. If we enter here then something went * wrong in our logic. */ ERROR_OUT(BAD_HELLO, exit_dch); } #endif /* Send a cookie */ if (!ssl->options.cookieGood && ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE) { #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { #ifdef WOLFSSL_DTLS13_NO_HRR_ON_RESUME /* We can skip cookie on resumption */ if (!ssl->options.dtls || !ssl->options.dtls13NoHrrOnResume || !args->usingPSK) #endif ERROR_OUT(BAD_HELLO, exit_dch); } else #endif { /* Need to remove the keyshare ext if we found a common group * and are not doing curve negotiation. */ TLSX_Remove(&ssl->extensions, TLSX_KEY_SHARE, ssl->heap); ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE; } } } #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_DTLS_CID /* do not modify CID state if we are sending an HRR */ if (ret == 0 && ssl->options.dtls && ssl->options.useDtlsCID && ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE) DtlsCIDOnExtensionsParsed(ssl); #endif /* WOLFSSL_DTLS_CID */ exit_dch: WOLFSSL_LEAVE("DoTls13ClientHello", ret); #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { ssl->msgsReceived.got_client_hello = 0; return ret; } #endif FreeDch13Args(ssl, args); #ifdef WOLFSSL_ASYNC_CRYPT FreeAsyncCtx(ssl, 0); /* Back to BEGIN so a later ClientHello (HRR, duplicate) can never be * mistaken for a replay and resume into freed args. */ ssl->options.asyncState = TLS_ASYNC_BEGIN; /* This ClientHello is done; a later one (HRR CH2, duplicate) must hash * itself afresh. */ ssl->options.chHashInput = 0; /* Replays skip the sanity check that re-sets got_client_hello; restore * on completion (only from 0: an HRR second ClientHello counts to 2). */ if (ret == 0 && ssl->msgsReceived.got_client_hello == 0) { ssl->msgsReceived.got_client_hello = 1; } #endif WOLFSSL_END(WC_FUNC_CLIENT_HELLO_DO); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(ret); } #if defined(HAVE_ECH) if (ret == 0 && echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE && ((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { /* add the header to the inner hello */ AddTls13HandShakeHeader(((WOLFSSL_ECH*)echX->data)->innerClientHello, ((WOLFSSL_ECH*)echX->data)->innerClientHelloLen, 0, 0, client_hello, ssl); } #endif return ret; } /* Send TLS v1.3 ServerHello message to client. * Only a server will send this message. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ /* handle generation of TLS 1.3 server_hello (2) */ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType) { int ret; byte* output; word16 length; word32 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; int sendSz; #if defined(HAVE_ECH) TLSX* echX = NULL; byte* acceptLabel = (byte*)echAcceptConfirmationLabel; word32 acceptOffset; word16 acceptLabelSz = ECH_ACCEPT_CONFIRMATION_LABEL_SZ; #endif WOLFSSL_START(WC_FUNC_SERVER_HELLO_SEND); WOLFSSL_ENTER("SendTls13ServerHello"); /* When ssl->options.dtlsStateful is not set then cookie is calculated in * dtls.c */ if (extMsgType == hello_retry_request #ifdef WOLFSSL_DTLS13 && (!ssl->options.dtls || ssl->options.dtlsStateful) #endif ) { WOLFSSL_MSG("wolfSSL Sending HelloRetryRequest"); if ((ret = RestartHandshakeHash(ssl)) < 0) return ret; } ssl->options.buildingMsg = 1; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) idx = DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ; #endif /* WOLFSSL_DTLS13 */ /* Protocol version, server random, session id, cipher suite, compression * and extensions. */ length = VERSION_SZ + RAN_LEN + ENUM_LEN + ssl->session->sessionIDSz + SUITE_LEN + COMP_LEN; ret = TLSX_GetResponseSize(ssl, extMsgType, &length); if (ret != 0) return ret; sendSz = (int)(idx + length); /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, sendSz)) != 0) return ret; /* Get position in output buffer to write new message to. */ output = GetOutputBuffer(ssl); /* Put the record and handshake headers on. */ AddTls13Headers(output, length, server_hello, ssl); /* The protocol version must be TLS v1.2 for middleboxes. */ output[idx++] = ssl->version.major; output[idx++] = ssl->options.dtls ? DTLSv1_2_MINOR : TLSv1_2_MINOR; if (extMsgType == server_hello) { /* Generate server random. */ if ((ret = wc_RNG_GenerateBlock(ssl->rng, output + idx, RAN_LEN)) != 0) return ret; } else { /* HelloRetryRequest message has fixed value for random. */ XMEMCPY(output + idx, helloRetryRequestRandom, RAN_LEN); } #if defined(HAVE_ECH) /* last 8 bytes of server random */ acceptOffset = idx + RAN_LEN - ECH_ACCEPT_CONFIRMATION_SZ; #endif /* Store in SSL for debugging. */ XMEMCPY(ssl->arrays->serverRandom, output + idx, RAN_LEN); idx += RAN_LEN; #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Server random"); WOLFSSL_BUFFER(ssl->arrays->serverRandom, RAN_LEN); #endif #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID) if (ssl->options.dtls) { /* RFC 9147 Section 5: "DTLS servers MUST NOT echo the * legacy_session_id value from the client." */ output[idx++] = 0; } else #endif { output[idx++] = ssl->session->sessionIDSz; if (ssl->session->sessionIDSz > 0) { XMEMCPY(output + idx, ssl->session->sessionID, ssl->session->sessionIDSz); idx += ssl->session->sessionIDSz; } } /* Chosen cipher suite */ output[idx++] = ssl->options.cipherSuite0; output[idx++] = ssl->options.cipherSuite; #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Chosen cipher suite:"); WOLFSSL_MSG(GetCipherNameInternal(ssl->options.cipherSuite0, ssl->options.cipherSuite)); #endif /* Compression not supported in TLS v1.3. */ output[idx++] = 0; /* Extensions */ ret = TLSX_WriteResponse(ssl, output + idx, extMsgType, NULL); if (ret != 0) return ret; /* When we send a HRR, we store the selected key share group to later check * that the client uses the same group in the second ClientHello. * * In case of stateless DTLS, we do not store the group, however, as it is * already stored in the cookie that is sent to the client. We later recover * the group from the cookie to prevent storing a state in a stateless * server. * * Similar logic holds for the hrrCipherSuite. */ if (extMsgType == hello_retry_request #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE) && (!ssl->options.dtls || ssl->options.dtlsStateful) #endif ) { TLSX* ksExt = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); if (ksExt != NULL) { KeyShareEntry* kse = (KeyShareEntry*)ksExt->data; if (kse != NULL) ssl->hrr_keyshare_group = kse->group; } ssl->options.hrrCipherSuite0 = ssl->options.cipherSuite0; ssl->options.hrrCipherSuite = ssl->options.cipherSuite; } #ifdef WOLFSSL_SEND_HRR_COOKIE if (ssl->options.sendCookie && extMsgType == hello_retry_request) { /* Reset the hashes from here. We will be able to restart the hashes * from the cookie in RestartHandshakeHashWithCookie */ #ifdef WOLFSSL_DTLS13 /* When ssl->options.dtlsStateful is not set then cookie is calculated * in dtls.c */ if (ssl->options.dtls && !ssl->options.dtlsStateful) ret = 0; else #endif ret = InitHandshakeHashes(ssl); } else #endif { #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Dtls13HashHandshake( ssl, output + Dtls13GetRlHeaderLength(ssl, 0) , (word16)sendSz - Dtls13GetRlHeaderLength(ssl, 0)); } else #endif /* WOLFSSL_DTLS13 */ { #if defined(HAVE_ECH) if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { echX = TLSX_Find(ssl->extensions, TLSX_ECH); if (echX == NULL) return WOLFSSL_FATAL_ERROR; /* use hrr offset */ if (extMsgType == hello_retry_request) { acceptOffset = (word32)(((WOLFSSL_ECH*)echX->data)->confBuf - output); acceptLabel = (byte*)echHrrAcceptConfirmationLabel; acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ; } /* replace the last 8 bytes of server random with the accept */ if (((WOLFSSL_ECH*)echX->data)->state == ECH_PARSED_INTERNAL) { if (ret == 0) { ret = EchWriteAcceptance(ssl, acceptLabel, acceptLabelSz, output + RECORD_HEADER_SZ, acceptOffset - RECORD_HEADER_SZ, sendSz - RECORD_HEADER_SZ, extMsgType); } if (extMsgType == hello_retry_request) { /* reset the ech state for round 2 */ ((WOLFSSL_ECH*)echX->data)->state = ECH_WRITE_NONE; /* inner hello no longer needed, free it */ XFREE(((WOLFSSL_ECH*)echX->data)->innerClientHello, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); ((WOLFSSL_ECH*)echX->data)->innerClientHello = NULL; } else { if (ret == 0) { /* update serverRandom on success */ XMEMCPY(ssl->arrays->serverRandom, output + acceptOffset - (RAN_LEN -ECH_ACCEPT_CONFIRMATION_SZ), RAN_LEN); } /* remove ech so we don't keep sending it in write */ TLSX_Remove(&ssl->extensions, TLSX_ECH, ssl->heap); } } } #endif if (ret == 0) ret = HashOutput(ssl, output, sendSz, 0); } } if (ret != 0) return ret; #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "ServerHello"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "ServerHello", handshake, output, sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) return ret; } #endif if (extMsgType == server_hello) ssl->options.serverState = SERVER_HELLO_COMPLETE; ssl->options.buildingMsg = 0; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Dtls13HandshakeSend(ssl, output, (word16)sendSz, (word16)sendSz, (enum HandShakeType)extMsgType, 0); WOLFSSL_LEAVE("SendTls13ServerHello", ret); WOLFSSL_END(WC_FUNC_SERVER_HELLO_SEND); return ret; } #endif /* WOLFSSL_DTLS13 */ ssl->buffers.outputBuffer.length += (word32)sendSz; if (!ssl->options.groupMessages || extMsgType != server_hello) ret = SendBuffered(ssl); WOLFSSL_LEAVE("SendTls13ServerHello", ret); WOLFSSL_END(WC_FUNC_SERVER_HELLO_SEND); return ret; } /* handle generation of TLS 1.3 encrypted_extensions (8) */ /* Send the rest of the extensions encrypted under the handshake key. * This message is always encrypted in TLS v1.3. * Only a server will send this message. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ static int SendTls13EncryptedExtensions(WOLFSSL* ssl) { int ret; byte* output; word16 length = 0; word32 idx; int sendSz; WOLFSSL_START(WC_FUNC_ENCRYPTED_EXTENSIONS_SEND); WOLFSSL_ENTER("SendTls13EncryptedExtensions"); ssl->options.buildingMsg = 1; ssl->keys.encryptionOn = 1; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { idx = Dtls13GetHeadersLength(ssl, encrypted_extensions); } else #endif /* WOLFSSL_DTLS13 */ { idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; } #ifdef WOLFSSL_ASYNC_CRYPT /* A suspended build already ran the key schedule below; re-running it * would extract in place over preMasterSecret a second time. */ if (ssl->options.buildArgs13Set) goto tls13_send_ee_build; #endif #if defined(HAVE_SUPPORTED_CURVES) && !defined(WOLFSSL_NO_SERVER_GROUPS_EXT) if ((ret = TLSX_SupportedCurve_CheckPriority(ssl)) != 0) return ret; #endif /* Derive the handshake secret now that we are at first message to be * encrypted under the keys. */ if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_NONE) { ret = DeriveHandshakeSecret(ssl); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_EE_HS_SECRET; } if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_EE_HS_SECRET) { ret = DeriveTls13Keys(ssl, handshake_key, ENCRYPT_AND_DECRYPT_SIDE, 1); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_EE_HS_KEYS; } /* Setup encrypt/decrypt keys for following messages. */ #ifdef WOLFSSL_EARLY_DATA if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_EE_HS_KEYS) { ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_EE_ENC_KEYS_SET; } if (ssl->earlyData != process_early_data) { if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_EE_ENC_KEYS_SET) { ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_EE_KEYS_SET; } } #else if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_EE_ENC_KEYS_SET) { ret = SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_EE_KEYS_SET; } #endif #ifdef WOLFSSL_QUIC if (IsAtLeastTLSv1_3(ssl->version) && WOLFSSL_IS_QUIC(ssl)) { ret = wolfSSL_quic_add_transport_extensions(ssl, encrypted_extensions); if (ret != 0) return ret; } #endif #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { w64wrapper epochHandshake = w64From32(0, DTLS13_EPOCH_HANDSHAKE); ssl->dtls13Epoch = epochHandshake; if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_EE_KEYS_SET) { ret = Dtls13SetEpochKeys(ssl, epochHandshake, ENCRYPT_AND_DECRYPT_SIDE); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_EE_DTLS_EPOCH; } } #endif /* WOLFSSL_DTLS13 */ ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; #ifdef WOLFSSL_ASYNC_CRYPT tls13_send_ee_build: #endif ret = TLSX_GetResponseSize(ssl, encrypted_extensions, &length); if (ret != 0) return ret; sendSz = (int)(idx + length); /* Encryption always on. */ sendSz += MAX_MSG_EXTRA; /* Check buffers are big enough and grow if needed. */ ret = CheckAvailableSize(ssl, sendSz); if (ret != 0) return ret; /* Get position in output buffer to write new message to. */ output = GetOutputBuffer(ssl); #ifdef WOLFSSL_ASYNC_CRYPT /* Skip on a resume: BuildTls13Message already replaced the record * header; rewriting the plaintext headers would corrupt it. */ if (!ssl->options.buildArgs13Set) #endif { /* Put the record and handshake headers on. */ AddTls13Headers(output, length, encrypted_extensions, ssl); ret = TLSX_WriteResponse(ssl, output + idx, encrypted_extensions, NULL); if (ret != 0) return ret; } idx += length; #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "EncryptedExtensions"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "EncryptedExtensions", handshake, output, sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) return ret; } #endif #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ssl->options.buildingMsg = 0; ret = Dtls13HandshakeSend(ssl, output, (word16)sendSz, (word16)idx, encrypted_extensions, 1); if (ret == 0) ssl->options.serverState = SERVER_ENCRYPTED_EXTENSIONS_COMPLETE; WOLFSSL_LEAVE("SendTls13EncryptedExtensions", ret); WOLFSSL_END(WC_FUNC_ENCRYPTED_EXTENSIONS_SEND); return ret; } #endif /* WOLFSSL_DTLS13 */ /* This handshake message is always encrypted. */ sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ, (int)(idx - RECORD_HEADER_SZ), handshake, 1, 0, TLS13_HS_ASYNC_OKAY); if (sendSz < 0) return sendSz; ssl->buffers.outputBuffer.length += (word32)sendSz; ssl->options.buildingMsg = 0; ssl->options.serverState = SERVER_ENCRYPTED_EXTENSIONS_COMPLETE; if (!ssl->options.groupMessages) ret = SendBuffered(ssl); WOLFSSL_LEAVE("SendTls13EncryptedExtensions", ret); WOLFSSL_END(WC_FUNC_ENCRYPTED_EXTENSIONS_SEND); return ret; } #ifndef NO_CERTS /* handle generation TLS v1.3 certificate_request (13) */ /* Send the TLS v1.3 CertificateRequest message. * This message is always encrypted in TLS v1.3. * Only a server will send this message. * * ssl SSL/TLS object. * reqCtx Request context. * reqCtxLen Length of context. 0 when sending as part of handshake. * returns 0 on success, otherwise failure. */ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, word32 reqCtxLen) { byte* output; int ret; int sendSz; word32 i; word32 reqSz; WOLFSSL_START(WC_FUNC_CERTIFICATE_REQUEST_SEND); WOLFSSL_ENTER("SendTls13CertificateRequest"); ssl->options.buildingMsg = 1; if (ssl->options.side != WOLFSSL_SERVER_END) return SIDE_ERROR; /* The extension writes WOLFSSL_SUITES(ssl), honoring set1_sigalgs_list. */ ret = TLSX_Push(&ssl->extensions, TLSX_SIGNATURE_ALGORITHMS, ssl, ssl->heap); if (ret != 0) return ret; i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) i = Dtls13GetRlHeaderLength(ssl, 1) + DTLS_HANDSHAKE_HEADER_SZ; #endif /* WOLFSSL_DTLS13 */ reqSz = (word16)(OPAQUE8_LEN + reqCtxLen); ret = TLSX_GetRequestSize(ssl, certificate_request, &reqSz); if (ret != 0) return ret; sendSz = (int)(i + reqSz); /* Always encrypted and make room for padding. */ sendSz += MAX_MSG_EXTRA; /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, sendSz)) != 0) return ret; /* Get position in output buffer to write new message to. */ output = GetOutputBuffer(ssl); /* Put the record and handshake headers on. */ AddTls13Headers(output, reqSz, certificate_request, ssl); /* Certificate request context. */ output[i++] = (byte)reqCtxLen; if (reqCtxLen != 0) { XMEMCPY(output + i, reqCtx, reqCtxLen); i += reqCtxLen; } /* Certificate extensions. */ reqSz = 0; ret = TLSX_WriteRequest(ssl, output + i, certificate_request, &reqSz); if (ret != 0) return ret; i += reqSz; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ssl->options.buildingMsg = 0; ret = Dtls13HandshakeSend(ssl, output, (word16)sendSz, (word16)i, certificate_request, 1); WOLFSSL_LEAVE("SendTls13CertificateRequest", ret); WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_SEND); return ret; } #endif /* WOLFSSL_DTLS13 */ /* Always encrypted. */ sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ, (int)(i - RECORD_HEADER_SZ), handshake, 1, 0, 0); if (sendSz < 0) return sendSz; #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "CertificateRequest"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "CertificateRequest", handshake, output, sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) return ret; } #endif ssl->buffers.outputBuffer.length += (word32)sendSz; ssl->options.buildingMsg = 0; if (!ssl->options.groupMessages) ret = SendBuffered(ssl); WOLFSSL_LEAVE("SendTls13CertificateRequest", ret); WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_SEND); return ret; } #endif /* NO_CERTS */ #endif /* NO_WOLFSSL_SERVER */ #ifndef NO_CERTS #if (!defined(NO_WOLFSSL_SERVER) || !defined(WOLFSSL_NO_CLIENT_AUTH)) && \ (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_ED448) || defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ defined(WOLFSSL_HAVE_SLHDSA)) /* Encode the signature algorithm into buffer. * * hashalgo The hash algorithm. * hsType The signature type. * output The buffer to encode into. */ static WC_INLINE void EncodeSigAlg(const WOLFSSL * ssl, byte hashAlgo, byte hsType, byte* output) { (void)ssl; (void)hashAlgo; switch (hsType) { #ifdef HAVE_ECC case ecc_dsa_sa_algo: if (ssl->pkCurveOID == ECC_BRAINPOOLP256R1_OID) { output[0] = NEW_SA_MAJOR; output[1] = ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR; } else if (ssl->pkCurveOID == ECC_BRAINPOOLP384R1_OID) { output[0] = NEW_SA_MAJOR; output[1] = ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR; } else if (ssl->pkCurveOID == ECC_BRAINPOOLP512R1_OID) { output[0] = NEW_SA_MAJOR; output[1] = ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR; } else { output[0] = hashAlgo; output[1] = ecc_dsa_sa_algo; } break; #endif #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) case sm2_sa_algo: output[0] = SM2_SA_MAJOR; output[1] = SM2_SA_MINOR; break; #endif #ifdef HAVE_ED25519 /* ED25519: 0x0807 */ case ed25519_sa_algo: output[0] = ED25519_SA_MAJOR; output[1] = ED25519_SA_MINOR; break; #endif #ifdef HAVE_ED448 /* ED448: 0x0808 */ case ed448_sa_algo: output[0] = ED448_SA_MAJOR; output[1] = ED448_SA_MINOR; break; #endif #ifndef NO_RSA /* PSS signatures: 0x080[4-6] or 0x080[9-B] */ case rsa_pss_sa_algo: output[0] = rsa_pss_sa_algo; #ifdef WC_RSA_PSS /* If the private key uses the RSA-PSS OID, and the peer supports * the rsa_pss_pss_* signature algorithm in use, then report * rsa_pss_pss_* rather than rsa_pss_rsae_*. */ if (ssl->useRsaPss && ((ssl->pssAlgo & (1U << hashAlgo)) != 0U) && (sha256_mac <= hashAlgo) && (hashAlgo <= sha512_mac)) { output[1] = PSS_RSAE_TO_PSS_PSS(hashAlgo); } else #endif { output[1] = hashAlgo; } break; #endif #ifdef HAVE_FALCON case falcon_level1_sa_algo: output[0] = FALCON_LEVEL1_SA_MAJOR; output[1] = FALCON_LEVEL1_SA_MINOR; break; case falcon_level5_sa_algo: output[0] = FALCON_LEVEL5_SA_MAJOR; output[1] = FALCON_LEVEL5_SA_MINOR; break; #endif #ifdef WOLFSSL_HAVE_MLDSA case mldsa_44_sa_algo: output[0] = MLDSA_44_SA_MAJOR; output[1] = MLDSA_44_SA_MINOR; break; case mldsa_65_sa_algo: output[0] = MLDSA_65_SA_MAJOR; output[1] = MLDSA_65_SA_MINOR; break; case mldsa_87_sa_algo: output[0] = MLDSA_87_SA_MAJOR; output[1] = MLDSA_87_SA_MINOR; break; #endif #ifdef WOLFSSL_HAVE_SLHDSA #if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S) case slhdsa_sha2_128s_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHA2_128S_SA_MINOR; break; #endif #if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F) case slhdsa_sha2_128f_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHA2_128F_SA_MINOR; break; #endif #if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S) case slhdsa_sha2_192s_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHA2_192S_SA_MINOR; break; #endif #if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F) case slhdsa_sha2_192f_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHA2_192F_SA_MINOR; break; #endif #if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S) case slhdsa_sha2_256s_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHA2_256S_SA_MINOR; break; #endif #if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F) case slhdsa_sha2_256f_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHA2_256F_SA_MINOR; break; #endif #ifdef WOLFSSL_SLHDSA_PARAM_128S case slhdsa_shake_128s_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHAKE_128S_SA_MINOR; break; #endif #ifdef WOLFSSL_SLHDSA_PARAM_128F case slhdsa_shake_128f_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHAKE_128F_SA_MINOR; break; #endif #ifdef WOLFSSL_SLHDSA_PARAM_192S case slhdsa_shake_192s_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHAKE_192S_SA_MINOR; break; #endif #ifdef WOLFSSL_SLHDSA_PARAM_192F case slhdsa_shake_192f_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHAKE_192F_SA_MINOR; break; #endif #ifdef WOLFSSL_SLHDSA_PARAM_256S case slhdsa_shake_256s_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHAKE_256S_SA_MINOR; break; #endif #ifdef WOLFSSL_SLHDSA_PARAM_256F case slhdsa_shake_256f_sa_algo: output[0] = SLHDSA_SA_MAJOR; output[1] = SLHDSA_SHAKE_256F_SA_MINOR; break; #endif #endif /* WOLFSSL_HAVE_SLHDSA */ default: break; } } #endif #if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_ED448) || defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ defined(WOLFSSL_HAVE_SLHDSA) #ifdef WOLFSSL_DUAL_ALG_CERTS /* These match up with what the OQS team has defined. */ #define HYBRID_SA_MAJOR 0xFE #define HYBRID_P256_MLDSA_44_SA_MINOR 0xA1 #define HYBRID_RSA3072_MLDSA_44_SA_MINOR 0xA2 #define HYBRID_P384_MLDSA_65_SA_MINOR 0xA4 #define HYBRID_P521_MLDSA_87_SA_MINOR 0xA6 /* Falcon hybrid codepoints aligned with oqs-provider. */ #define HYBRID_P256_FALCON_LEVEL1_SA_MINOR 0xD8 #define HYBRID_RSA3072_FALCON_LEVEL1_SA_MINOR 0xD9 #define HYBRID_P521_FALCON_LEVEL5_SA_MINOR 0xDB /* Custom defined ones for PQC first */ #define HYBRID_MLDSA_44_P256_SA_MINOR 0xD1 #define HYBRID_MLDSA_44_RSA3072_SA_MINOR 0xD2 #define HYBRID_MLDSA_65_P384_SA_MINOR 0xD3 #define HYBRID_MLDSA_87_P521_SA_MINOR 0xD4 #define HYBRID_FALCON_LEVEL1_P256_SA_MINOR 0xD5 #define HYBRID_FALCON_LEVEL1_RSA3072_SA_MINOR 0xD6 #define HYBRID_FALCON_LEVEL5_P521_SA_MINOR 0xD7 static void EncodeDualSigAlg(byte sigAlg, byte altSigAlg, byte* output) { /* Initialize output to error indicator. */ output[0] = 0x0; output[1] = 0x0; if (sigAlg == ecc_dsa_sa_algo && altSigAlg == mldsa_44_sa_algo) { output[1] = HYBRID_P256_MLDSA_44_SA_MINOR; } else if (sigAlg == rsa_pss_sa_algo && altSigAlg == mldsa_44_sa_algo) { output[1] = HYBRID_RSA3072_MLDSA_44_SA_MINOR; } else if (sigAlg == ecc_dsa_sa_algo && altSigAlg == mldsa_65_sa_algo) { output[1] = HYBRID_P384_MLDSA_65_SA_MINOR; } else if (sigAlg == ecc_dsa_sa_algo && altSigAlg == mldsa_87_sa_algo) { output[1] = HYBRID_P521_MLDSA_87_SA_MINOR; } else if (sigAlg == ecc_dsa_sa_algo && altSigAlg == falcon_level1_sa_algo) { output[1] = HYBRID_P256_FALCON_LEVEL1_SA_MINOR; } else if (sigAlg == rsa_pss_sa_algo && altSigAlg == falcon_level1_sa_algo) { output[1] = HYBRID_RSA3072_FALCON_LEVEL1_SA_MINOR; } else if (sigAlg == ecc_dsa_sa_algo && altSigAlg == falcon_level5_sa_algo) { output[1] = HYBRID_P521_FALCON_LEVEL5_SA_MINOR; } else if (sigAlg == mldsa_44_sa_algo && altSigAlg == ecc_dsa_sa_algo) { output[1] = HYBRID_MLDSA_44_P256_SA_MINOR; } else if (sigAlg == mldsa_44_sa_algo && altSigAlg == rsa_pss_sa_algo) { output[1] = HYBRID_MLDSA_44_RSA3072_SA_MINOR; } else if (sigAlg == mldsa_65_sa_algo && altSigAlg == ecc_dsa_sa_algo) { output[1] = HYBRID_MLDSA_65_P384_SA_MINOR; } else if (sigAlg == mldsa_87_sa_algo && altSigAlg == ecc_dsa_sa_algo) { output[1] = HYBRID_MLDSA_87_P521_SA_MINOR; } else if (sigAlg == falcon_level1_sa_algo && altSigAlg == ecc_dsa_sa_algo) { output[1] = HYBRID_FALCON_LEVEL1_P256_SA_MINOR; } else if (sigAlg == falcon_level1_sa_algo && altSigAlg == rsa_pss_sa_algo) { output[1] = HYBRID_FALCON_LEVEL1_RSA3072_SA_MINOR; } else if (sigAlg == falcon_level5_sa_algo && altSigAlg == ecc_dsa_sa_algo) { output[1] = HYBRID_FALCON_LEVEL5_P521_SA_MINOR; } if (output[1] != 0x0) { output[0] = HYBRID_SA_MAJOR; } } #endif /* WOLFSSL_DUAL_ALG_CERTS */ static enum wc_MACAlgorithm GetNewSAHashAlgo(int typeIn) { switch (typeIn) { case RSA_PSS_RSAE_SHA256_MINOR: case RSA_PSS_PSS_SHA256_MINOR: case ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR: return sha256_mac; case RSA_PSS_RSAE_SHA384_MINOR: case RSA_PSS_PSS_SHA384_MINOR: case ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR: return sha384_mac; case RSA_PSS_RSAE_SHA512_MINOR: case RSA_PSS_PSS_SHA512_MINOR: case ED25519_SA_MINOR: case ED448_SA_MINOR: case ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR: return sha512_mac; default: return no_mac; } } /* Decode the signature algorithm. * * input The encoded signature algorithm. * hashalgo The hash algorithm. * hsType The signature type. * returns INVALID_PARAMETER if not recognized and 0 otherwise. */ static WC_INLINE int DecodeTls13SigAlg(byte* input, byte* hashAlgo, byte* hsType) { int ret = 0; #if defined(WOLFSSL_HAVE_SLHDSA) byte slhType; #endif switch (input[0]) { #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) case SM2_SA_MAJOR: if (input[1] == SM2_SA_MINOR) { *hsType = sm2_sa_algo; *hashAlgo = sm3_mac; } else ret = INVALID_PARAMETER; break; #endif case NEW_SA_MAJOR: { enum wc_MACAlgorithm mac = GetNewSAHashAlgo(input[1]); *hashAlgo = (byte)mac; } /* PSS encryption: 0x080[4-6] */ if (input[1] >= RSA_PSS_RSAE_SHA256_MINOR && input[1] <= RSA_PSS_RSAE_SHA512_MINOR) { *hsType = input[0]; } /* PSS signature: 0x080[9-B] */ else if (input[1] >= RSA_PSS_PSS_SHA256_MINOR && input[1] <= RSA_PSS_PSS_SHA512_MINOR) { *hsType = input[0]; } #ifdef HAVE_ED25519 /* ED25519: 0x0807 */ else if (input[1] == ED25519_SA_MINOR) { *hsType = ed25519_sa_algo; /* Hash performed as part of sign/verify operation. */ } #endif #ifdef HAVE_ED448 /* ED448: 0x0808 */ else if (input[1] == ED448_SA_MINOR) { *hsType = ed448_sa_algo; /* Hash performed as part of sign/verify operation. */ } #endif #ifdef HAVE_ECC_BRAINPOOL else if ((input[1] == ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR) || (input[1] == ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR) || (input[1] == ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR)) { *hsType = ecc_dsa_sa_algo; } #endif else ret = INVALID_PARAMETER; break; #if defined(HAVE_FALCON) case FALCON_SA_MAJOR: if (input[1] == FALCON_LEVEL1_SA_MINOR) { *hsType = falcon_level1_sa_algo; /* Hash performed as part of sign/verify operation. */ *hashAlgo = sha512_mac; } else if (input[1] == FALCON_LEVEL5_SA_MINOR) { *hsType = falcon_level5_sa_algo; /* Hash performed as part of sign/verify operation. */ *hashAlgo = sha512_mac; } else ret = INVALID_PARAMETER; break; #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA) /* ML-DSA and SLH-DSA share the same major byte (0x09); their minor * bytes are disjoint (ML-DSA 0x04-0x06, SLH-DSA 0x11-0x1C). */ case MLDSA_SA_MAJOR: ret = INVALID_PARAMETER; #if defined(WOLFSSL_HAVE_MLDSA) if (input[1] == MLDSA_44_SA_MINOR) { *hsType = mldsa_44_sa_algo; /* Hash performed as part of sign/verify operation. */ *hashAlgo = sha512_mac; ret = 0; } else if (input[1] == MLDSA_65_SA_MINOR) { *hsType = mldsa_65_sa_algo; *hashAlgo = sha512_mac; ret = 0; } else if (input[1] == MLDSA_87_SA_MINOR) { *hsType = mldsa_87_sa_algo; *hashAlgo = sha512_mac; ret = 0; } #endif /* WOLFSSL_HAVE_MLDSA */ #if defined(WOLFSSL_HAVE_SLHDSA) if (ret != 0) { slhType = SlhDsaSigMinorToType(input[1]); if (slhType != (byte)invalid_sa_algo) { *hsType = slhType; /* Hash performed as part of sign/verify operation. */ *hashAlgo = sha512_mac; ret = 0; } } #endif /* WOLFSSL_HAVE_SLHDSA */ break; #endif /* WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */ default: *hashAlgo = input[0]; *hsType = input[1]; break; } return ret; } #ifdef WOLFSSL_DUAL_ALG_CERTS /* Decode the hybrid signature algorithm. * * input The encoded signature algorithm. * hashalgo The hash algorithm. * hsType The signature type. * returns INVALID_PARAMETER if not recognized and 0 otherwise. */ static WC_INLINE int DecodeTls13HybridSigAlg(byte* input, byte* hashAlg, byte *sigAlg, byte *altSigAlg) { if (input[0] != HYBRID_SA_MAJOR) { return INVALID_PARAMETER; } if (input[1] == HYBRID_P256_MLDSA_44_SA_MINOR) { *sigAlg = ecc_dsa_sa_algo; *hashAlg = sha256_mac; *altSigAlg = mldsa_44_sa_algo; } else if (input[1] == HYBRID_RSA3072_MLDSA_44_SA_MINOR) { *sigAlg = rsa_pss_sa_algo; *hashAlg = sha256_mac; *altSigAlg = mldsa_44_sa_algo; } else if (input[1] == HYBRID_P384_MLDSA_65_SA_MINOR) { *sigAlg = ecc_dsa_sa_algo; *hashAlg = sha384_mac; *altSigAlg = mldsa_65_sa_algo; } else if (input[1] == HYBRID_P521_MLDSA_87_SA_MINOR) { *sigAlg = ecc_dsa_sa_algo; *hashAlg = sha512_mac; *altSigAlg = mldsa_87_sa_algo; } else if (input[1] == HYBRID_P256_FALCON_LEVEL1_SA_MINOR) { *sigAlg = ecc_dsa_sa_algo; *hashAlg = sha256_mac; *altSigAlg = falcon_level1_sa_algo; } else if (input[1] == HYBRID_RSA3072_FALCON_LEVEL1_SA_MINOR) { *sigAlg = rsa_pss_sa_algo; *hashAlg = sha256_mac; *altSigAlg = falcon_level1_sa_algo; } else if (input[1] == HYBRID_P521_FALCON_LEVEL5_SA_MINOR) { *sigAlg = ecc_dsa_sa_algo; *hashAlg = sha512_mac; *altSigAlg = falcon_level5_sa_algo; } else if (input[1] == HYBRID_MLDSA_44_P256_SA_MINOR) { *sigAlg = mldsa_44_sa_algo; *hashAlg = sha256_mac; *altSigAlg = ecc_dsa_sa_algo; } else if (input[1] == HYBRID_MLDSA_44_RSA3072_SA_MINOR) { *sigAlg = mldsa_44_sa_algo; *hashAlg = sha256_mac; *altSigAlg = rsa_pss_sa_algo; } else if (input[1] == HYBRID_MLDSA_65_P384_SA_MINOR) { *sigAlg = mldsa_65_sa_algo; *hashAlg = sha384_mac; *altSigAlg = ecc_dsa_sa_algo; } else if (input[1] == HYBRID_MLDSA_87_P521_SA_MINOR) { *sigAlg = mldsa_87_sa_algo; *hashAlg = sha512_mac; *altSigAlg = ecc_dsa_sa_algo; } else if (input[1] == HYBRID_FALCON_LEVEL1_P256_SA_MINOR) { *sigAlg = falcon_level1_sa_algo; *hashAlg = sha256_mac; *altSigAlg = ecc_dsa_sa_algo; } else if (input[1] == HYBRID_FALCON_LEVEL1_RSA3072_SA_MINOR) { *sigAlg = falcon_level1_sa_algo; *hashAlg = sha256_mac; *altSigAlg = rsa_pss_sa_algo; } else if (input[1] == HYBRID_FALCON_LEVEL5_P521_SA_MINOR) { *sigAlg = falcon_level5_sa_algo; *hashAlg = sha512_mac; *altSigAlg = ecc_dsa_sa_algo; } else { return INVALID_PARAMETER; } return 0; } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* Get the hash of the messages so far. * * ssl The SSL/TLS object. * hash The buffer to write the hash to. * returns the length of the hash. */ static WC_INLINE int GetMsgHash(WOLFSSL* ssl, byte* hash) { int ret = 0; switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: ret = wc_Sha256GetHash(&ssl->hsHashes->hashSha256, hash); if (ret == 0) ret = WC_SHA256_DIGEST_SIZE; break; #endif /* !NO_SHA256 */ #ifdef WOLFSSL_SHA384 case sha384_mac: ret = wc_Sha384GetHash(&ssl->hsHashes->hashSha384, hash); if (ret == 0) ret = WC_SHA384_DIGEST_SIZE; break; #endif /* WOLFSSL_SHA384 */ #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: ret = wc_Sha512GetHash(&ssl->hsHashes->hashSha512, hash); if (ret == 0) ret = WC_SHA512_DIGEST_SIZE; break; #endif /* WOLFSSL_TLS13_SHA512 */ #ifdef WOLFSSL_SM3 case sm3_mac: ret = wc_Sm3GetHash(&ssl->hsHashes->hashSm3, hash); if (ret == 0) ret = WC_SM3_DIGEST_SIZE; break; #endif /* WOLFSSL_SM3 */ default: break; } return ret; } /* The server certificate verification label. */ static const byte serverCertVfyLabel[CERT_VFY_LABEL_SZ] = "TLS 1.3, server CertificateVerify"; /* The client certificate verification label. */ static const byte clientCertVfyLabel[CERT_VFY_LABEL_SZ] = "TLS 1.3, client CertificateVerify"; /* The prefix byte in the signature data. */ #define SIGNING_DATA_PREFIX_BYTE 0x20 /* Create the signature data for TLS v1.3 certificate verification. * * ssl The SSL/TLS object. * sigData The signature data. * sigDataSz The length of the signature data. * check Indicates this is a check not create. */ int CreateSigData(WOLFSSL* ssl, byte* sigData, word16* sigDataSz, int check) { word16 idx; int side = ssl->options.side; int ret; /* Signature Data = Prefix | Label | Handshake Hash */ XMEMSET(sigData, SIGNING_DATA_PREFIX_BYTE, SIGNING_DATA_PREFIX_SZ); idx = SIGNING_DATA_PREFIX_SZ; if ((side == WOLFSSL_SERVER_END && check) || (side == WOLFSSL_CLIENT_END && !check)) { XMEMCPY(&sigData[idx], clientCertVfyLabel, CERT_VFY_LABEL_SZ); } if ((side == WOLFSSL_CLIENT_END && check) || (side == WOLFSSL_SERVER_END && !check)) { XMEMCPY(&sigData[idx], serverCertVfyLabel, CERT_VFY_LABEL_SZ); } idx += CERT_VFY_LABEL_SZ; ret = GetMsgHash(ssl, &sigData[idx]); if (ret < 0) return ret; if (ret == 0) return HASH_TYPE_E; *sigDataSz = (word16)(idx + ret); ret = 0; return ret; } #ifndef NO_RSA /* Encode the PKCS #1.5 RSA signature. * * sig The buffer to place the encoded signature into. * sigData The data to be signed. * sigDataSz The size of the data to be signed. * hashAlgo The hash algorithm to use when signing. * returns the length of the encoded signature or negative on error. */ int CreateRSAEncodedSig(byte* sig, byte* sigData, int sigDataSz, int sigAlgo, int hashAlgo) { Digest digest; int hashSz = 0; int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); byte* hash; (void)sigAlgo; hash = sig; /* Digest the signature data. */ switch (hashAlgo) { #ifndef NO_SHA256 case sha256_mac: ret = wc_InitSha256(&digest.sha256); if (ret == 0) { ret = wc_Sha256Update(&digest.sha256, sigData, (word32)sigDataSz); if (ret == 0) ret = wc_Sha256Final(&digest.sha256, hash); wc_Sha256Free(&digest.sha256); } hashSz = WC_SHA256_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: ret = wc_InitSha384(&digest.sha384); if (ret == 0) { ret = wc_Sha384Update(&digest.sha384, sigData, (word32)sigDataSz); if (ret == 0) ret = wc_Sha384Final(&digest.sha384, hash); wc_Sha384Free(&digest.sha384); } hashSz = WC_SHA384_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA512 case sha512_mac: ret = wc_InitSha512(&digest.sha512); if (ret == 0) { ret = wc_Sha512Update(&digest.sha512, sigData, (word32)sigDataSz); if (ret == 0) ret = wc_Sha512Final(&digest.sha512, hash); wc_Sha512Free(&digest.sha512); } hashSz = WC_SHA512_DIGEST_SIZE; break; #endif default: ret = BAD_FUNC_ARG; break; } if (ret != 0) return ret; return hashSz; } #endif /* !NO_RSA */ #ifdef HAVE_ECC /* Encode the ECC signature. * * sigData The data to be signed. * sigDataSz The size of the data to be signed. * hashAlgo The hash algorithm to use when signing. * returns the length of the encoded signature or negative on error. */ static int CreateECCEncodedSig(byte* sigData, int sigDataSz, int hashAlgo) { Digest digest; int hashSz = 0; int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); /* Digest the signature data. */ switch (hashAlgo) { #ifndef NO_SHA256 case sha256_mac: ret = wc_InitSha256(&digest.sha256); if (ret == 0) { ret = wc_Sha256Update(&digest.sha256, sigData, (word32)sigDataSz); if (ret == 0) ret = wc_Sha256Final(&digest.sha256, sigData); wc_Sha256Free(&digest.sha256); } hashSz = WC_SHA256_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: ret = wc_InitSha384(&digest.sha384); if (ret == 0) { ret = wc_Sha384Update(&digest.sha384, sigData, (word32)sigDataSz); if (ret == 0) ret = wc_Sha384Final(&digest.sha384, sigData); wc_Sha384Free(&digest.sha384); } hashSz = WC_SHA384_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA512 case sha512_mac: ret = wc_InitSha512(&digest.sha512); if (ret == 0) { ret = wc_Sha512Update(&digest.sha512, sigData, (word32)sigDataSz); if (ret == 0) ret = wc_Sha512Final(&digest.sha512, sigData); wc_Sha512Free(&digest.sha512); } hashSz = WC_SHA512_DIGEST_SIZE; break; #endif default: ret = BAD_FUNC_ARG; break; } if (ret != 0) return ret; return hashSz; } #endif /* HAVE_ECC */ #if !defined(NO_RSA) && defined(WC_RSA_PSS) /* Check that the decrypted signature matches the encoded signature * based on the digest of the signature data. * * ssl The SSL/TLS object. * sigAlgo The signature algorithm used to generate signature. * hashAlgo The hash algorithm used to generate signature. * decSig The decrypted signature. * decSigSz The size of the decrypted signature. * returns 0 on success, otherwise failure. */ static int CheckRSASignature(WOLFSSL* ssl, int sigAlgo, int hashAlgo, byte* decSig, word32 decSigSz) { int ret = 0; byte sigData[MAX_SIG_DATA_SZ]; word16 sigDataSz; ret = CreateSigData(ssl, sigData, &sigDataSz, 1); if (ret != 0) return ret; if (sigAlgo == rsa_pss_sa_algo) { enum wc_HashType hashType = WC_HASH_TYPE_NONE; word32 sigSz; ret = ConvertHashPss(hashAlgo, &hashType, NULL); if (ret < 0) return ret; /* PSS signature can be done in-place */ ret = CreateRSAEncodedSig(sigData, sigData, sigDataSz, sigAlgo, hashAlgo); if (ret < 0) return ret; sigSz = (word32)ret; ret = wc_RsaPSS_CheckPadding(sigData, sigSz, decSig, decSigSz, hashType); } return ret; } #endif /* !NO_RSA && WC_RSA_PSS */ #endif /* !NO_RSA || HAVE_ECC */ #if !defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER) /* Get the next certificate from the list for writing into the TLS v1.3 * Certificate message. * * data The certificate list. * length The length of the certificate data in the list. * idx The index of the next certificate. * returns the length of the certificate data. 0 indicates no more certificates * in the list. */ static word32 NextCert(byte* data, word32 length, word32* idx) { word32 len; /* Would index read past end of list? */ if (*idx + 3 > length) return 0; /* Length of the current ASN.1 encoded certificate. */ c24to32(data + *idx, &len); /* Include the length field. */ len += 3; /* Ensure len does not overrun certificate list */ if (*idx + len > length) return 0; /* Move index to next certificate and return the current certificate's * length. */ *idx += len; return len; } #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(NO_WOLFSSL_SERVER) /* Write certificate status request into certificate to buffer. * * ssl SSL/TLS object. * certExts DerBuffer array. buffers written * extSz word32 array. * Length of the certificate status request data for the certificate. * extSz_num number of the CSR written * extIdx The index number of certificate status request data * for the certificate. * offset index offset * returns Total number of bytes written on success or negative value on error. */ static int WriteCSRToBuffer(WOLFSSL* ssl, DerBuffer** certExts, word16* extSz, word16 extSz_num) { int ret = 0; TLSX* ext; CertificateStatusRequest* csr; word32 ex_offset = HELLO_EXT_TYPE_SZ + OPAQUE16_LEN /* extension type */ + OPAQUE16_LEN /* extension length */; word32 totalSz = 0; word32 tmpSz; word32 extIdx; DerBuffer* der; if (extSz_num > MAX_CERT_EXTENSIONS) return MAX_CERT_EXTENSIONS_ERR; ext = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); csr = ext ? (CertificateStatusRequest*)ext->data : NULL; if (csr) { for (extIdx = 0; extIdx < (word16)(extSz_num); extIdx++) { tmpSz = TLSX_CSR_GetSize_ex(csr, 0, (int)extIdx); if (ssl->fragOffset != 0 && certExts[extIdx] != NULL) { /* A fragmented send is being resumed and this buffer was * written by the earlier call. extSz starts over on every * call, so recover this entry's size from the length written * into the buffer. */ ato16(certExts[extIdx]->buffer, &extSz[extIdx]); extSz[extIdx] += OPAQUE16_LEN; } else { /* Not a resume, so anything still allocated here is left over * from a completed message and must not be reused. */ FreeDer(&certExts[extIdx]); if (tmpSz > (OPAQUE8_LEN + OPAQUE24_LEN)) { /* csr extension is not zero */ if (tmpSz > WOLFSSL_MAX_16BIT) return BUFFER_E; extSz[extIdx] = (word16)tmpSz; ret = AllocDer(&certExts[extIdx], extSz[extIdx] + ex_offset, CERT_TYPE, ssl->heap); if (ret < 0) return ret; der = certExts[extIdx]; /* write extension type */ c16toa(ext->type, der->buffer + OPAQUE16_LEN); /* writes extension data length. */ c16toa(extSz[extIdx], der->buffer + HELLO_EXT_TYPE_SZ + OPAQUE16_LEN); /* write extension data */ extSz[extIdx] = (word16)TLSX_CSR_Write_ex(csr, der->buffer + ex_offset, 0, extIdx); /* add extension offset */ extSz[extIdx] += (word16)ex_offset; /* extension length */ c16toa(extSz[extIdx] - OPAQUE16_LEN, der->buffer); } } totalSz += extSz[extIdx]; } } else { /* chain cert empty extension size */ totalSz += OPAQUE16_LEN * extSz_num; } return (int)totalSz; } #endif /* HAVE_CERTIFICATE_STATUS_REQUEST */ /* Add certificate data and empty extension to output up to the fragment size. * * ssl SSL/TLS object. * cert The certificate data to write out. * len The length of the certificate data. * extSz Length of the extension data with the certificate. * idx The start of the certificate data to write out. * fragSz The maximum size of this fragment. * output The buffer to write to. * extIdx The index number of the extension data with the certificate * returns the number of bytes written. */ static word32 AddCertExt(WOLFSSL* ssl, byte* cert, word32 len, word16 extSz, word32 idx, word32 fragSz, byte* output, word16 extIdx) { word32 i = 0; word32 copySz = min(len - idx, fragSz); if (idx < len) { XMEMCPY(output, cert + idx, copySz); i = copySz; if (copySz == fragSz) return i; } copySz = len + extSz - idx - i; if (extSz == OPAQUE16_LEN) { if (copySz <= fragSz) { /* Empty extension */ output[i++] = 0; output[i++] = 0; } } else { byte* certExts = ssl->buffers.certExts[extIdx]->buffer + idx + i - len; /* Put out as much of the extensions' data as will fit in fragment. */ if (copySz > fragSz - i) copySz = fragSz - i; XMEMCPY(output + i, certExts, copySz); i += copySz; } return i; } #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(NO_WOLFSSL_SERVER) static int SetupOcspResp(WOLFSSL* ssl) { DecodedCert* cert = NULL; CertificateStatusRequest* csr = NULL; TLSX* extension = NULL; int ret = 0; OcspRequest* request = NULL; byte ctxOwnsRequest = 0; extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); #ifdef WOLFSSL_POST_HANDSHAKE_AUTH /* During post-handshake client authentication the client must staple * its own OCSP response, but the status_request extension it offered in * the initial ClientHello is no longer present on ssl->extensions. * Recreate it here (the request extension still lives on * ctx->extensions). Pass ssl so csr->ssl is set, which the response * size/write path requires. */ if (extension == NULL && ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.handShakeDone && TLSX_Find(ssl->ctx->extensions, TLSX_STATUS_REQUEST) != NULL) { ret = TLSX_UseCertificateStatusRequest(&ssl->extensions, WOLFSSL_CSR_OCSP, 0, ssl, ssl->heap, ssl->devId); if (ret != WOLFSSL_SUCCESS) return ret; extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); } #endif if (extension == NULL) return 0; /* peer didn't signal ocsp support */ csr = (CertificateStatusRequest*)extension->data; if (csr == NULL) return MEMORY_ERROR; if (SSL_CM(ssl) != NULL && SSL_CM(ssl)->ocsp_stapling != NULL && SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) { return TLSX_CSR_SetResponseWithStatusCB(ssl); } if (ssl->buffers.certificate == NULL) { WOLFSSL_MSG("Certificate buffer not set!"); return BUFFER_ERROR; } cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), ssl->heap, DYNAMIC_TYPE_DCERT); if (cert == NULL) { return MEMORY_E; } InitDecodedCert(cert, ssl->buffers.certificate->buffer, ssl->buffers.certificate->length, ssl->heap); ret = ParseCert(cert, CERT_TYPE, NO_VERIFY, SSL_CM(ssl)); if (ret != 0) { FreeDecodedCert(cert); XFREE(cert, ssl->heap, DYNAMIC_TYPE_DCERT); return ret; } #ifdef WOLFSSL_POST_HANDSHAKE_AUTH /* On client PHA the same certificate is re-stapled every round; reuse * request slot 0 instead of appending, else csr->requests grows until * MAX_CERT_EXTENSIONS_ERR. */ if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.handShakeDone) { ret = TLSX_CSR_InitRequest_ex(ssl->extensions, cert, ssl->heap, 0); } else #endif { ret = TLSX_CSR_InitRequest(ssl->extensions, cert, ssl->heap); } FreeDecodedCert(cert); XFREE(cert, ssl->heap, DYNAMIC_TYPE_DCERT); if (ret != 0 ) return ret; /* Free previous OCSP response buffers to avoid leak on PHA reuse */ { int j; for (j = 0; j < MAX_CERT_EXTENSIONS; j++) { XFREE(csr->responses[j].buffer, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); csr->responses[j].buffer = NULL; csr->responses[j].length = 0; } } request = &csr->request.ocsp[0]; ret = CreateOcspResponse(ssl, &request, &csr->responses[0], &ctxOwnsRequest); /* Only a successful call replaces "request", and only a request the CTX did * not take ownership of is ours to free. Both are checked, matching the * SendCertificateStatus() callers. */ if (ret == 0 && request != &csr->request.ocsp[0] && !ctxOwnsRequest) { /* request was allocated in CreateOcspResponse() */ FreeOcspRequest(request); XFREE(request, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST); } if (ret != 0) return ret; if (csr->responses[0].buffer) extension->resp = 1; #if defined(WOLFSSL_TLS_OCSP_MULTI) /* process OCSP request in certificate chain */ if ((ret = ProcessChainOCSPRequest(ssl)) != 0) { WOLFSSL_MSG("Process Cert Chain OCSP request failed"); WOLFSSL_ERROR_VERBOSE(ret); return ret; } #endif return ret; } #endif #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) /* Certificate is signed with the deprecated SHA-1 hash. An unrecognized or * unparsable algorithm is not SHA-1; the peer still verifies the chain. * * der Buffer holding the DER encoded certificate. * derSz Length of the DER encoded certificate. * returns 1 when SHA-1 signed, 0 otherwise. */ static int IsSha1SignedCert(const byte* der, word32 derSz) { word32 idx = 0; word32 oid = 0; word32 algoIdEnd = 0; int len = 0; int isSha1 = 0; int ret; #if defined(WC_RSA_PSS) && !defined(NO_RSA) enum wc_HashType hash = WC_HASH_TYPE_NONE; int mgf = 0; int saltLen = 0; #endif /* Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, ... }. * GetSequence() checks each length against the maximum index passed in, so * idx and idx + len stay inside the buffer. */ ret = GetSequence(der, &idx, &len, derSz); if (ret >= 0) ret = GetSequence(der, &idx, &len, derSz); if (ret >= 0) { /* signatureAlgorithm immediately follows the tbsCertificate. Decode * the AlgorithmIdentifier here rather than with GetAlgoId() so the * RSASSA-PSS parameters, which hold the digest, stay reachable. */ idx += (word32)len; ret = GetSequence(der, &idx, &len, derSz); } if (ret >= 0) { algoIdEnd = idx + (word32)len; ret = GetObjectId(der, &idx, &oid, oidSigType, algoIdEnd); } if (ret >= 0) { if ((oid == CTC_SHAwRSA) || (oid == CTC_SHAwECDSA) || (oid == CTC_SHAwDSA)) { isSha1 = 1; } #if defined(WC_RSA_PSS) && !defined(NO_RSA) /* RSASSA-PSS uses one signature OID for every digest and names the * digest in the algorithm parameters instead. Absent parameters are * passed through as a zero length buffer rather than skipped: RFC 4055 * makes them mean all defaults, which is SHA-1, and * wc_DecodeRsaPssParams() reports that. */ else if ((oid == RSAPSSk) && (idx <= algoIdEnd) && (wc_DecodeRsaPssParams(der + idx, algoIdEnd - idx, &hash, &mgf, &saltLen) == 0)) { isSha1 = (hash == WC_HASH_TYPE_SHA); } #endif } return isSha1; } /* Certificate is self issued. RFC 8446 Section 4.4.2.2: "Certificates that are * self-signed or certificates that are expected to be trust anchors are not * validated as part of the chain and therefore MAY be signed with any * algorithm." * * The rule is applied to self-issued certificates (issuer name equals subject * name), which is enough here: the chain is the one this end was configured * with, not one an attacker supplies. * * The certificate is parsed as CA_TYPE rather than CERT_TYPE so a trust anchor * carrying serial number 0 still decodes. ParseCertRelative() rejects a zero * serial for CERT_TYPE, and legacy roots, the certificates most likely to be * SHA-1 signed, are the ones that use it. With NO_VERIFY and no certificate * manager the serial exemption is all the type changes, and that exemption * still requires a self-issued CA, so a leaf carrying serial 0 is reported as * not self issued and stays subject to the SHA-1 rule. * * ssl The SSL/TLS object. * der Buffer holding the DER encoded certificate. * derSz Length of the DER encoded certificate. * isSelfIssued On success, 1 when self issued, 0 otherwise. A certificate * that will not parse is reported as not self issued so the * SHA-1 rule still applies to it. * returns 0 on success, MEMORY_E when the decoder cannot be allocated. */ static int IsSelfIssuedCert(WOLFSSL* ssl, const byte* der, word32 derSz, int* isSelfIssued) { DecodedCert* cert; *isSelfIssued = 0; cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), ssl->heap, DYNAMIC_TYPE_DCERT); if (cert == NULL) return MEMORY_E; InitDecodedCert(cert, der, derSz, ssl->heap); if (ParseCertRelative(cert, CA_TYPE, NO_VERIFY, NULL, NULL) == 0) *isSelfIssued = (cert->selfIssued != 0); else WOLFSSL_MSG("Cannot decode certificate, not treating as self issued"); FreeDecodedCert(cert); XFREE(cert, ssl->heap, DYNAMIC_TYPE_DCERT); return 0; } /* Check the chain about to be sent against what the peer advertised. * * RFC 8446 Section 4.4.2.2 permits a fallback chain the peer did not advertise * support for, but the chain "MUST NOT" use SHA-1 unless the peer's * advertisement permits it. Section 4.4.2.3 requires client certificates to be * signed with an acceptable algorithm "as described in Section 4.4.2.2", so the * same rule covers both sides. How a failure is resolved differs by side and is * left to the caller. * * ssl The SSL/TLS object. * returns 0 when the chain may be sent, MATCH_SUITE_ERROR when it may not and * MEMORY_E when a certificate could not be examined. */ static int CheckCertChainSigAlgo(WOLFSSL* ssl) { byte* chain; byte* cur; word32 chainSz; word32 len; word32 idx = 0; int selfIssued = 0; int ret = 0; if (ssl->options.peerSha1CertOk) return 0; if (ssl->buffers.certificate == NULL || ssl->buffers.certificate->buffer == NULL) { return 0; } if (IsSha1SignedCert(ssl->buffers.certificate->buffer, ssl->buffers.certificate->length)) { ret = IsSelfIssuedCert(ssl, ssl->buffers.certificate->buffer, ssl->buffers.certificate->length, &selfIssued); if (ret != 0) return ret; if (!selfIssued) ret = MATCH_SUITE_ERROR; } if (ret == 0 && ssl->buffers.certChain != NULL && ssl->buffers.certChain->buffer != NULL && ssl->buffers.certChainCnt > 0) { chain = ssl->buffers.certChain->buffer; chainSz = ssl->buffers.certChain->length; while (ret == 0) { cur = chain + idx; /* NextCert() length includes the CERT_HEADER_SZ byte prefix and * is 0 at the end of the list. Keep this terminator matching the * send loop so both walk the same certificates. */ len = NextCert(chain, chainSz, &idx); if (len == 0) break; if (len <= CERT_HEADER_SZ) continue; cur += CERT_HEADER_SZ; len -= CERT_HEADER_SZ; if (IsSha1SignedCert(cur, len)) { ret = IsSelfIssuedCert(ssl, cur, len, &selfIssued); if (ret != 0) return ret; if (!selfIssued) ret = MATCH_SUITE_ERROR; } } } if (ret == WC_NO_ERR_TRACE(MATCH_SUITE_ERROR)) WOLFSSL_MSG("Chain is SHA-1 signed but peer did not advertise SHA-1"); return ret; } #endif /* !NO_CERTS && !WOLFSSL_NO_SIGALG */ /* handle generation TLS v1.3 certificate (11) */ /* Send the certificate for this end and any CAs that help with validation. * This message is always encrypted in TLS v1.3. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ static int SendTls13Certificate(WOLFSSL* ssl) { int ret = 0; word32 certSz, certChainSz, headerSz, listSz, payloadSz; word16 extSz[MAX_CERT_EXTENSIONS]; word16 extIdx = 0; word32 maxFragment; word32 totalextSz = 0; word32 len = 0; word32 idx = 0; word32 offset = 0; word32 entrySz = 0; byte* p = NULL; byte certReqCtxLen = 0; sword32 length; #ifdef WOLFSSL_POST_HANDSHAKE_AUTH byte* certReqCtx = NULL; #endif #ifndef WOLFSSL_NO_SIGALG int chainRet; #endif #ifdef OPENSSL_EXTRA WOLFSSL_X509* x509 = NULL; WOLFSSL_EVP_PKEY* pkey = NULL; #endif WOLFSSL_START(WC_FUNC_CERTIFICATE_SEND); WOLFSSL_ENTER("SendTls13Certificate"); XMEMSET(extSz, 0, sizeof(extSz)); ssl->options.buildingMsg = 1; #ifdef WOLFSSL_POST_HANDSHAKE_AUTH if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->certReqCtx != NULL) { certReqCtxLen = ssl->certReqCtx->len; certReqCtx = &ssl->certReqCtx->ctx; } #endif #if defined(OPENSSL_EXTRA) && defined(WOLFSSL_CERT_SETUP_CB) /* call client cert callback if no cert has been loaded */ if ((ssl->ctx->CBClientCert != NULL) && (!ssl->buffers.certificate || !ssl->buffers.certificate->buffer)) { ret = ssl->ctx->CBClientCert(ssl, &x509, &pkey); if (ret == 1) { if ((wolfSSL_CTX_use_certificate(ssl->ctx, x509) == WOLFSSL_SUCCESS) && (wolfSSL_CTX_use_PrivateKey(ssl->ctx, pkey) == WOLFSSL_SUCCESS)) { ssl->options.sendVerify = SEND_CERT; } wolfSSL_X509_free(x509); x509 = NULL; wolfSSL_EVP_PKEY_free(pkey); } } #endif #ifndef WOLFSSL_NO_SIGALG /* Run before the blank certificate branch below so a client with nothing * acceptable can fall into it. Only on first entry: fragOffset is reset to * 0 before this message is built and is non-zero only while resuming a * fragmented send, whose chain was checked on the first pass. The result is * kept out of ret so a pending value there is left alone. */ if (ssl->options.sendVerify != SEND_BLANK_CERT && ssl->fragOffset == 0) { chainRet = CheckCertChainSigAlgo(ssl); if ((chainRet != 0) && (chainRet != WC_NO_ERR_TRACE(MATCH_SUITE_ERROR))) { return chainRet; } if (chainRet == WC_NO_ERR_TRACE(MATCH_SUITE_ERROR)) { if (ssl->options.side == WOLFSSL_SERVER_END) { SendAlert(ssl, alert_fatal, handshake_failure); WOLFSSL_ERROR_VERBOSE(MATCH_SUITE_ERROR); return MATCH_SUITE_ERROR; } #ifndef WOLFSSL_NO_CLIENT_CERT_ERROR /* RFC 8446 Section 4.4.2: a client with no acceptable certificate * sends an empty certificate_list rather than failing. */ WOLFSSL_MSG("Client chain not acceptable, sending blank cert"); ssl->options.sendVerify = SEND_BLANK_CERT; #else /* RFC 8446 Section 4.4.2.2: an endpoint that cannot produce an * acceptable chain aborts with a certificate related alert, * unsupported_certificate by default. */ WOLFSSL_MSG("Client chain not acceptable and blank cert not " "allowed"); SendAlert(ssl, alert_fatal, unsupported_certificate); WOLFSSL_ERROR_VERBOSE(NO_CERT_ERROR); return NO_CERT_ERROR; #endif } } #endif if (ssl->options.sendVerify == SEND_BLANK_CERT) { certSz = 0; certChainSz = 0; headerSz = OPAQUE8_LEN + certReqCtxLen + CERT_HEADER_SZ; length = (sword32)headerSz; listSz = 0; } else { if (!ssl->buffers.certificate || !ssl->buffers.certificate->buffer) { WOLFSSL_MSG("Send Cert missing certificate buffer"); return NO_CERT_ERROR; } /* Certificate Data */ certSz = ssl->buffers.certificate->length; if (ssl->buffers.certChainCnt > MAX_CHAIN_DEPTH) { WOLFSSL_MSG("Certificate chain count exceeds maximum depth"); return MAX_CHAIN_ERROR; } /* Cert Req Ctx Len | Cert Req Ctx | Cert List Len | Cert Data Len */ headerSz = OPAQUE8_LEN + certReqCtxLen + CERT_HEADER_SZ + CERT_HEADER_SZ; /* set empty extension as default */ for (extIdx = 0; extIdx < (word16)XELEM_CNT(extSz); extIdx++) extSz[extIdx] = OPAQUE16_LEN; #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(NO_WOLFSSL_SERVER) /* Staple our own OCSP response with the Certificate. Normally only the * server staples; the client's CSR holds the server's response, so * echoing it back is wrong. The exception is post-handshake auth (PHA), * where the client sends its own Certificate and staples for it. */ if (ssl->options.side == WOLFSSL_SERVER_END #ifdef WOLFSSL_POST_HANDSHAKE_AUTH || (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.handShakeDone) #endif ) { /* Build the responses once. A resumed send reuses them: looking * them up again appends another set of requests to the extension * until it overflows with MAX_CERT_EXTENSIONS_ERR. */ if (ssl->fragOffset == 0) { ret = SetupOcspResp(ssl); if (ret != 0) return ret; } if ((1 + ssl->buffers.certChainCnt) > MAX_CERT_EXTENSIONS) ret = MAX_CERT_EXTENSIONS_ERR; if (ret == 0) ret = WriteCSRToBuffer(ssl, &ssl->buffers.certExts[0], &extSz[0], 1 /* +1 for leaf */ + (word16)ssl->buffers.certChainCnt); if (ret < 0) return ret; totalextSz += ret; ret = 0; /* Clear to signal no error */ } else #endif { /* Leaf cert empty extension size */ totalextSz += OPAQUE16_LEN; /* chain cert empty extension size */ totalextSz += OPAQUE16_LEN * ssl->buffers.certChainCnt; } /* Length of message data with one certificate and extensions. */ length = (sword32)(headerSz + certSz + totalextSz); /* Length of list data with one certificate and extensions. */ listSz = CERT_HEADER_SZ + certSz + totalextSz; /* Send rest of chain if sending cert (chain has leading size/s). */ if (certSz > 0 && ssl->buffers.certChainCnt > 0) { p = ssl->buffers.certChain->buffer; /* Chain length including extensions. */ certChainSz = ssl->buffers.certChain->length; length += certChainSz; listSz += certChainSz; } else certChainSz = 0; } payloadSz = (word32)length; if (ssl->fragOffset != 0) length -= (ssl->fragOffset + headerSz); maxFragment = (word32)wolfssl_local_GetMaxPlaintextSize(ssl); extIdx = 0; /* Only ssl->fragOffset survives a WANT_WRITE, so a resume inside the chain * has to rebuild the walk cursor from it. */ if (certChainSz > 0 && ssl->fragOffset >= certSz + extSz[0]) { word32 chainPos = ssl->fragOffset - (certSz + extSz[0]); #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(NO_WOLFSSL_SERVER) /* The leaf is behind us and its buffer was rebuilt above. */ FreeDer(&ssl->buffers.certExts[0]); #endif while (chainPos > 0) { word32 prevIdx = idx; len = NextCert(ssl->buffers.certChain->buffer, ssl->buffers.certChain->length, &idx); if (len == 0) break; #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ !defined(NO_WOLFSSL_SERVER) if (extIdx + 1 < MAX_CERT_EXTENSIONS) extIdx++; #endif entrySz = len + extSz[extIdx]; if (chainPos < entrySz) { /* Resume part way through this entry. */ p = ssl->buffers.certChain->buffer + prevIdx; offset = chainPos; chainPos = 0; } else { /* Entry already sent in full; stay primed for the next one. */ #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ !defined(NO_WOLFSSL_SERVER) /* Its buffer was rebuilt above and nothing writes it again. */ FreeDer(&ssl->buffers.certExts[extIdx]); #endif chainPos -= entrySz; offset = 0; entrySz = 0; } } } while (length > 0 && ret == 0) { byte* output = NULL; word32 fragSz = 0; word32 i = RECORD_HEADER_SZ; int sendSz = RECORD_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { i = Dtls13GetRlHeaderLength(ssl, 1); sendSz = (int)i; } #endif /* WOLFSSL_DTLS13 */ if (ssl->fragOffset == 0) { if (headerSz + certSz + totalextSz + certChainSz <= maxFragment - HANDSHAKE_HEADER_SZ) { fragSz = headerSz + certSz + totalextSz + certChainSz; } #ifdef WOLFSSL_DTLS13 else if (ssl->options.dtls){ /* short-circuit the fragmentation logic here. DTLS fragmentation will be done in dtls13HandshakeSend() */ fragSz = headerSz + certSz + totalextSz + certChainSz; } #endif /* WOLFSSL_DTLS13 */ else { fragSz = maxFragment - HANDSHAKE_HEADER_SZ; } sendSz += fragSz + HANDSHAKE_HEADER_SZ; i += HANDSHAKE_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { sendSz += DTLS_HANDSHAKE_EXTRA; i += DTLS_HANDSHAKE_EXTRA; } #endif /* WOLFSSL_DTLS13 */ } else { fragSz = min((word32)length, maxFragment); sendSz += fragSz; } sendSz += MAX_MSG_EXTRA; /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, sendSz)) != 0) return ret; /* Get position in output buffer to write new message to. */ output = GetOutputBuffer(ssl); if (ssl->fragOffset == 0) { AddTls13FragHeaders(output, fragSz, 0, payloadSz, certificate, ssl); /* Request context. */ output[i++] = certReqCtxLen; #ifdef WOLFSSL_POST_HANDSHAKE_AUTH if (certReqCtxLen > 0) { XMEMCPY(output + i, certReqCtx, certReqCtxLen); i += certReqCtxLen; } #endif length -= OPAQUE8_LEN + certReqCtxLen; fragSz -= OPAQUE8_LEN + certReqCtxLen; /* Certificate list length. */ c32to24(listSz, output + i); i += CERT_HEADER_SZ; length -= CERT_HEADER_SZ; fragSz -= CERT_HEADER_SZ; /* Leaf certificate data length. */ if (certSz > 0) { c32to24(certSz, output + i); i += CERT_HEADER_SZ; length -= CERT_HEADER_SZ; fragSz -= CERT_HEADER_SZ; } } else AddTls13RecordHeader(output, fragSz, handshake, ssl); if (extIdx == 0) { if (certSz > 0 && ssl->fragOffset < certSz + extSz[0]) { /* Put in the leaf certificate with extensions. */ word32 copySz = AddCertExt(ssl, ssl->buffers.certificate->buffer, certSz, extSz[0], ssl->fragOffset, fragSz, output + i, 0); i += copySz; ssl->fragOffset += copySz; length -= copySz; fragSz -= copySz; if (ssl->fragOffset == certSz + extSz[0]) FreeDer(&ssl->buffers.certExts[0]); } } if (certChainSz > 0 && fragSz > 0) { /* Put in the CA certificates with extensions. */ while (fragSz > 0) { word32 l; if (offset == entrySz) { /* Find next CA certificate to write out. */ offset = 0; /* Point to the start of current cert in chain buffer. */ p = ssl->buffers.certChain->buffer + idx; len = NextCert(ssl->buffers.certChain->buffer, ssl->buffers.certChain->length, &idx); if (len == 0) break; #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ !defined(NO_WOLFSSL_SERVER) if (extIdx + 1 < MAX_CERT_EXTENSIONS) extIdx++; #endif /* Certificate and its extensions make up the entry. */ entrySz = len + extSz[extIdx]; } /* Write out certificate and extension. */ l = AddCertExt(ssl, p, len, extSz[extIdx], offset, fragSz, output + i, extIdx); i += l; ssl->fragOffset += l; length -= l; fragSz -= l; offset += l; if (extIdx != 0 && extIdx < MAX_CERT_EXTENSIONS && ssl->buffers.certExts[extIdx] != NULL && offset == entrySz) { FreeDer(&ssl->buffers.certExts[extIdx]); } } } if ((int)i - RECORD_HEADER_SZ < 0) { WOLFSSL_MSG("Send Cert bad inputSz"); return BUFFER_E; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { /* DTLS1.3 uses a separate variable and logic for fragments */ ssl->options.buildingMsg = 0; ssl->fragOffset = 0; if ((word32)sendSz > WOLFSSL_MAX_16BIT || i > WOLFSSL_MAX_16BIT) { WOLFSSL_MSG("Send Cert DTLS size exceeds word16"); return BUFFER_E; } ret = Dtls13HandshakeSend(ssl, output, (word16)sendSz, (word16)i, certificate, 1); } else #endif /* WOLFSSL_DTLS13 */ { /* This message is always encrypted. */ sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ, (int)(i - RECORD_HEADER_SZ), handshake, 1, 0, 0); if (sendSz < 0) return sendSz; #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "Certificate"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "Certificate", handshake, output, sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) return ret; } #endif ssl->buffers.outputBuffer.length += (word32)sendSz; ssl->options.buildingMsg = 0; if (!ssl->options.groupMessages) ret = SendBuffered(ssl); } } if (ret != WC_NO_ERR_TRACE(WANT_WRITE)) { /* Clean up the fragment offset. */ ssl->options.buildingMsg = 0; ssl->fragOffset = 0; if (ssl->options.side == WOLFSSL_SERVER_END) ssl->options.serverState = SERVER_CERT_COMPLETE; } #ifdef WOLFSSL_POST_HANDSHAKE_AUTH if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->certReqCtx != NULL) { CertReqCtx* ctx = ssl->certReqCtx; ssl->certReqCtx = ssl->certReqCtx->next; XFREE(ctx, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); } #endif WOLFSSL_LEAVE("SendTls13Certificate", ret); WOLFSSL_END(WC_FUNC_CERTIFICATE_SEND); return ret; } #if (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_ED448) || defined(HAVE_FALCON) || \ defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA)) && \ (!defined(NO_WOLFSSL_SERVER) || !defined(WOLFSSL_NO_CLIENT_AUTH)) /* Members are grouped widest first so the struct carries no interior padding. * Under WOLFSSL_ASYNC_CRYPT this must fit ssl->async->args (MAX_ASYNC_ARGS * word32s), which the static assert in SendTls13CertificateVerify enforces. */ typedef struct Scv13Args { byte* output; /* not allocated */ byte* verify; /* not allocated */ byte* sigData; #ifndef NO_RSA byte* toSign; /* not allocated */ #endif #ifdef WOLFSSL_DUAL_ALG_CERTS byte* altSigData; #endif /* Fragmented CertificateVerify send cursor, used when the signature does * not fit in a single TLS record (SLH-DSA and other oversized signatures). * Kept in the async args so a WC_PENDING_E from the record AEAD under * WOLFSSL_ASYNC_CRYPT resumes on the same fragment instead of re-emitting * the records already committed to the output buffer. */ byte* frag; /* body copy, freed by FreeScv13Args; NULL when idle */ word32 idx; word32 sigLen; int sendSz; word32 length; #ifndef NO_RSA word32 toSignSz; #endif #ifdef WOLFSSL_DUAL_ALG_CERTS word32 altSigLen; /* Only used in the case of both native and alt. */ #endif word32 outputSz; /* reserved capacity of the output record buffer */ word32 fragOffset; /* body bytes already committed to records */ word16 sigDataSz; #ifdef WOLFSSL_DUAL_ALG_CERTS word16 altSigDataSz; #endif byte sigAlgo; #ifdef WOLFSSL_DUAL_ALG_CERTS byte altSigAlgo; #endif byte fragActive; /* current fragment laid out, record build may pend */ } Scv13Args; static void FreeScv13Args(WOLFSSL* ssl, void* pArgs) { Scv13Args* args = (Scv13Args*)pArgs; (void)ssl; if (args && args->sigData) { XFREE(args->sigData, ssl->heap, DYNAMIC_TYPE_SIGNATURE); args->sigData = NULL; } #ifdef WOLFSSL_DUAL_ALG_CERTS if (args && args->altSigData != NULL) { XFREE(args->altSigData, ssl->heap, DYNAMIC_TYPE_SIGNATURE); args->altSigData = NULL; } #endif if (args != NULL && args->frag != NULL) { XFREE(args->frag, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); args->frag = NULL; } } /* handle generation TLS v1.3 certificate_verify (15) */ /* Send the TLS v1.3 CertificateVerify message. * A hash of all the message so far is used. * The signed data is: * 0x20 * 64 | context string | 0x00 | hash of messages * This message is always encrypted in TLS v1.3. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ static int SendTls13CertificateVerify(WOLFSSL* ssl) { int ret = 0; #ifndef NO_RSA /* Use this as a temporary buffer for RSA signature verification. */ buffer* rsaSigBuf = &ssl->buffers.sig; #endif #ifdef WOLFSSL_ASYNC_CRYPT Scv13Args* args = NULL; WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args); #else Scv13Args args[1]; #endif #ifdef WOLFSSL_DTLS13 int recordLayerHdrExtra; #endif /* WOLFSSL_DTLS13 */ WOLFSSL_START(WC_FUNC_CERTIFICATE_VERIFY_SEND); WOLFSSL_ENTER("SendTls13CertificateVerify"); #ifdef WOLFSSL_BLIND_PRIVATE_KEY wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask); #endif ssl->options.buildingMsg = 1; #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13SendCertVerify(ssl); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { goto exit_scv; } ret = 0; #endif /* WOLFSSL_RENESAS_TSIP_TLS */ #ifdef WOLFSSL_DTLS13 /* can be negative */ if (ssl->options.dtls) recordLayerHdrExtra = Dtls13GetRlHeaderLength(ssl, 1) - RECORD_HEADER_SZ; else recordLayerHdrExtra = 0; #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_ASYNC_CRYPT if (ssl->async == NULL) { ssl->async = (struct WOLFSSL_ASYNC*) XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap, DYNAMIC_TYPE_ASYNC); if (ssl->async == NULL) ERROR_OUT(MEMORY_E, exit_scv); } args = (Scv13Args*)ssl->async->args; ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* Check for error */ if (ret < 0) goto exit_scv; } else #endif { /* Reset state */ ret = 0; ssl->options.asyncState = TLS_ASYNC_BEGIN; XMEMSET(args, 0, sizeof(Scv13Args)); #ifdef WOLFSSL_ASYNC_CRYPT ssl->async->freeArgs = FreeScv13Args; #endif } #ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY /* Resuming a partially-sent streamed CertificateVerify (e.g. after a * non-blocking WANT_WRITE): the signature is already assembled in * ssl->buffers.certVerifyMsg and ssl->fragOffset marks how much has been * committed to records, so skip re-signing and continue sending. */ if (ssl->buffers.certVerifyMsg.buffer != NULL && ssl->fragOffset != 0) { ssl->options.asyncState = TLS_ASYNC_END; } #endif switch(ssl->options.asyncState) { case TLS_ASYNC_BEGIN: { if (ssl->options.sendVerify == SEND_BLANK_CERT) { #ifdef WOLFSSL_BLIND_PRIVATE_KEY wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask); #endif return 0; /* sent blank cert, can't verify */ } /* The output buffer is reserved in TLS_ASYNC_BUILD, once the * private key has been decoded and the actual signature size is * known. This avoids reserving the worst-case WC_MAX_CERT_VERIFY_SZ * (very large when SLH-DSA is enabled) for every algorithm. */ /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_BUILD; } /* case TLS_ASYNC_BEGIN */ FALL_THROUGH; case TLS_ASYNC_BUILD: { int rem; #ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY word32 bodySz; word32 maxFrag; #endif int doStream = 0; /* Decode the private key first so the output buffer can be sized * to the actual signature length rather than the worst-case * WC_MAX_CERT_VERIFY_SZ (very large when SLH-DSA is enabled). */ if (ssl->buffers.key == NULL) { #ifdef HAVE_PK_CALLBACKS if (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)) args->sigLen = (word32)GetPrivateKeySigSize(ssl); else #endif ERROR_OUT(NO_PRIVATE_KEY, exit_scv); } else { #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE) { /* In the case of alternative, we swap in the alt. */ if (ssl->buffers.altKey == NULL) { ERROR_OUT(NO_PRIVATE_KEY, exit_scv); } ssl->buffers.keyType = ssl->buffers.altKeyType; ssl->buffers.keySz = ssl->buffers.altKeySz; /* If we own it, free key before overriding it. */ if (ssl->buffers.weOwnKey) { FreeDer(&ssl->buffers.key); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.keyMask); #endif } /* Swap keys */ ssl->buffers.key = ssl->buffers.altKey; ssl->buffers.weOwnKey = ssl->buffers.weOwnAltKey; /* buffers.key is the only owner of the alt key now. */ ssl->buffers.weOwnAltKey = 0; #ifdef WOLFSSL_BLIND_PRIVATE_KEY ssl->buffers.keyMask = ssl->buffers.altKeyMask; /* Unblind the alternative key before decoding */ wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask); #endif } #endif /* WOLFSSL_DUAL_ALG_CERTS */ ret = DecodePrivateKey(ssl, &args->sigLen); if (ret != 0) goto exit_scv; } if (args->sigLen == 0) { ERROR_OUT(NO_PRIVATE_KEY, exit_scv); } #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->peerSigSpec == NULL) { /* The peer did not respond. We didn't send CKS or they don't * support it. Either way, we do not need to handle dual * key/sig case. */ ssl->sigSpec = NULL; ssl->sigSpecSz = 0; } if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* The native was already decoded. Decode the alternative now * too so its signature length is included when the output * buffer is sized below. */ if (ssl->buffers.altKey == NULL) { ERROR_OUT(NO_PRIVATE_KEY, exit_scv); } /* After this call, args->altSigLen has the length we need for * the alternative signature. */ ret = DecodeAltPrivateKey(ssl, &args->altSigLen); if (ret != 0) goto exit_scv; if (ssl->buffers.altKeyType == ecc_dsa_sa_algo || ssl->buffers.altKeyType == falcon_level1_sa_algo || ssl->buffers.altKeyType == falcon_level5_sa_algo || ssl->buffers.altKeyType == mldsa_44_sa_algo || ssl->buffers.altKeyType == mldsa_65_sa_algo || ssl->buffers.altKeyType == mldsa_87_sa_algo) { args->altSigAlgo = ssl->buffers.altKeyType; } else if (ssl->buffers.altKeyType == rsa_sa_algo && ssl->hsAltType == DYNAMIC_TYPE_RSA) { args->altSigAlgo = rsa_pss_sa_algo; } else { ERROR_OUT(ALGO_ID_E, exit_scv); } } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* Decide how the CertificateVerify body will be emitted. When it * exceeds a single record (e.g. a large SLH-DSA/ML-DSA signature, * or any signature under a small max_fragment_length) on TLS 1.3, * use the streaming path: generate the signature into a * connection-level body buffer and send it one record at a time in * TLS_ASYNC_END, so the shared output buffer never has to hold the * whole signature. Otherwise reserve the output buffer for the * whole message and build it in place. */ #ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY bodySz = HASH_SIG_SIZE + VERIFY_HEADER + (word32)args->sigLen; #ifdef WOLFSSL_DUAL_ALG_CERTS /* A dual (BOTH) body carries a second length-prefixed signature. * args->sigLen/altSigLen are upper bounds here for variable-length * schemes (ECDSA, Falcon), so this may over-size the body buffer by * a few bytes; the body is assembled contiguously with the actual * lengths, the exact length is recorded in TLS_ASYNC_FINALIZE, and * the trailing slack is never sent. */ if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { bodySz += (word32)args->altSigLen + OPAQUE16_LEN + OPAQUE16_LEN; } #endif maxFrag = (word32)wolfssl_local_GetMaxPlaintextSize(ssl); if (!ssl->options.dtls && (word32)HANDSHAKE_HEADER_SZ + bodySz > maxFrag) { doStream = 1; } #endif /* WOLFSSL_TLS13_STREAM_CERT_VERIFY */ if (doStream) { #ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY /* Generate the body directly into the connection-level buffer. * bodySz is exact here (a single fixed-or-max-length * signature); the final length is recorded in * TLS_ASYNC_FINALIZE. args->output stays NULL to mark the * streaming path; the send loop reserves one record at a * time. */ XFREE(ssl->buffers.certVerifyMsg.buffer, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); ssl->buffers.certVerifyMsg.buffer = (byte*)XMALLOC(bodySz, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (ssl->buffers.certVerifyMsg.buffer == NULL) { ssl->buffers.certVerifyMsg.length = 0; ERROR_OUT(MEMORY_E, exit_scv); } ssl->buffers.certVerifyMsg.length = bodySz; ssl->fragOffset = 0; args->verify = ssl->buffers.certVerifyMsg.buffer; args->idx = 0; args->sendSz = (int)bodySz; args->output = NULL; args->outputSz = 0; #endif /* WOLFSSL_TLS13_STREAM_CERT_VERIFY */ } else { /* Reserve the output buffer, sized from the actual signature * length(s) plus record/handshake framing and encryption * slack. */ args->sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + HASH_SIG_SIZE + VERIFY_HEADER + (int)args->sigLen; #ifdef WOLFSSL_DUAL_ALG_CERTS /* A dual (BOTH) body carries a second signature behind its own * length prefix, plus the combined length prefix. Matches the * streaming bodySz above. */ if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { args->sendSz += (int)args->altSigLen + OPAQUE16_LEN + OPAQUE16_LEN; } else { args->sendSz += (int)args->altSigLen; } #endif #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) args->sendSz += recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA; #endif /* WOLFSSL_DTLS13 */ /* Framing and always-on encryption expansion. */ args->sendSz += MAX_MSG_EXTRA; /* check for available size */ if ((ret = CheckAvailableSize(ssl, args->sendSz)) != 0) { goto exit_scv; } /* get output buffer */ args->output = GetOutputBuffer(ssl); /* Remember the reserved capacity for BuildTls13Message / * Dtls13HandshakeSend in TLS_ASYNC_END. */ args->outputSz = (word32)args->sendSz; rem = (int)(ssl->buffers.outputBuffer.bufferSize - ssl->buffers.outputBuffer.length - RECORD_HEADER_SZ - HANDSHAKE_HEADER_SZ); /* idx is used to track verify pointer offset to output */ args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; args->verify = &args->output[RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ]; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { rem -= recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA; args->idx += recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA; args->verify += recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA; } #endif /* WOLFSSL_DTLS13 */ if (rem < 0 || (int)args->sigLen > rem) { ERROR_OUT(BUFFER_E, exit_scv); } } /* Add signature algorithm. */ if (ssl->hsType == DYNAMIC_TYPE_RSA) args->sigAlgo = rsa_pss_sa_algo; #ifdef HAVE_ECC else if (ssl->hsType == DYNAMIC_TYPE_ECC) { #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) if (ssl->buffers.keyType == sm2_sa_algo) { args->sigAlgo = sm2_sa_algo; } else #endif { args->sigAlgo = ecc_dsa_sa_algo; } } #endif #ifdef HAVE_ED25519 else if (ssl->hsType == DYNAMIC_TYPE_ED25519) args->sigAlgo = ed25519_sa_algo; #endif #ifdef HAVE_ED448 else if (ssl->hsType == DYNAMIC_TYPE_ED448) args->sigAlgo = ed448_sa_algo; #endif #if defined(HAVE_FALCON) else if (ssl->hsType == DYNAMIC_TYPE_FALCON) { args->sigAlgo = ssl->buffers.keyType; } #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_MLDSA) else if (ssl->hsType == DYNAMIC_TYPE_MLDSA) { args->sigAlgo = ssl->buffers.keyType; } #endif /* WOLFSSL_HAVE_MLDSA */ #if defined(WOLFSSL_HAVE_SLHDSA) else if (ssl->hsType == DYNAMIC_TYPE_SLHDSA) { args->sigAlgo = ssl->buffers.keyType; } #endif /* WOLFSSL_HAVE_SLHDSA */ else { ERROR_OUT(ALGO_ID_E, exit_scv); } #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* The alternative key was already decoded and its sig-alg * determined above (when sizing the output buffer). Encode the * dual (native + alternative) signature algorithm pair. */ EncodeDualSigAlg(args->sigAlgo, args->altSigAlgo, args->verify); if (args->verify[0] == 0) { ERROR_OUT(ALGO_ID_E, exit_scv); } } else #endif /* WOLFSSL_DUAL_ALG_CERTS */ EncodeSigAlg(ssl, ssl->options.hashAlgo, args->sigAlgo, args->verify); if (args->sigData == NULL) { word32 sigLen = MAX_SIG_DATA_SZ; if ((ssl->hsType == DYNAMIC_TYPE_RSA) && (args->sigLen > MAX_SIG_DATA_SZ)) { /* We store the RSA signature in the sigData buffer * temporarily, hence its size must be fitting. */ sigLen = args->sigLen; } args->sigData = (byte*)XMALLOC(sigLen, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (args->sigData == NULL) { ERROR_OUT(MEMORY_E, exit_scv); } } #ifdef WOLFSSL_DUAL_ALG_CERTS if ((ssl->sigSpec != NULL) && (*ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) && (args->altSigData == NULL)) { word32 sigLen = MAX_SIG_DATA_SZ; if (ssl->hsAltType == DYNAMIC_TYPE_RSA && args->altSigLen > MAX_SIG_DATA_SZ) { /* We store the RSA signature in the sigData buffer * temporarily, hence its size must be fitting. */ sigLen = args->altSigLen; } args->altSigData = (byte*)XMALLOC(sigLen, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (args->altSigData == NULL) { ERROR_OUT(MEMORY_E, exit_scv); } } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* Create the data to be signed. */ ret = CreateSigData(ssl, args->sigData, &args->sigDataSz, 0); if (ret != 0) goto exit_scv; #ifdef WOLFSSL_DUAL_ALG_CERTS if ((ssl->sigSpec != NULL) && (*ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH)) { XMEMCPY(args->altSigData, args->sigData, args->sigDataSz); args->altSigDataSz = args->sigDataSz; } #endif /* WOLFSSL_DUAL_ALG_CERTS */ #ifndef NO_RSA if (ssl->hsType == DYNAMIC_TYPE_RSA) { /* build encoded signature buffer */ rsaSigBuf->length = WC_MAX_DIGEST_SIZE; rsaSigBuf->buffer = (byte*)XMALLOC(rsaSigBuf->length, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (rsaSigBuf->buffer == NULL) { ERROR_OUT(MEMORY_E, exit_scv); } ret = CreateRSAEncodedSig(rsaSigBuf->buffer, args->sigData, args->sigDataSz, args->sigAlgo, ssl->options.hashAlgo); if (ret < 0) goto exit_scv; rsaSigBuf->length = (unsigned int)ret; ret = 0; } #endif /* !NO_RSA */ #ifdef HAVE_ECC if (ssl->hsType == DYNAMIC_TYPE_ECC) { args->sigLen = (word32)args->sendSz - args->idx - HASH_SIG_SIZE - VERIFY_HEADER; #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) if (ssl->buffers.keyType != sm2_sa_algo) #endif { ret = CreateECCEncodedSig(args->sigData, args->sigDataSz, ssl->options.hashAlgo); if (ret < 0) goto exit_scv; args->sigDataSz = (word16)ret; ret = 0; } } #endif /* HAVE_ECC */ #ifdef HAVE_ED25519 if (ssl->hsType == DYNAMIC_TYPE_ED25519) { ret = Ed25519CheckPubKey(ssl); if (ret < 0) { ERROR_OUT(ret, exit_scv); } args->sigLen = ED25519_SIG_SIZE; } #endif /* HAVE_ED25519 */ #ifdef HAVE_ED448 if (ssl->hsType == DYNAMIC_TYPE_ED448) { ret = Ed448CheckPubKey(ssl); if (ret < 0) { ERROR_OUT(ret, exit_scv); } args->sigLen = ED448_SIG_SIZE; } #endif /* HAVE_ED448 */ #if defined(HAVE_FALCON) if (ssl->hsType == DYNAMIC_TYPE_FALCON) { /* Per-key, not the family maximum: this is handed to the * signer as the output capacity and the buffer above was * reserved from this key's signature length. */ int fSigSz = wc_falcon_sig_size((falcon_key*)ssl->hsKey); if (fSigSz <= 0) { ERROR_OUT(ALGO_ID_E, exit_scv); } args->sigLen = (word32)fSigSz; } #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) if (ssl->hsType == DYNAMIC_TYPE_MLDSA) { int mSigSz = wc_MlDsaKey_SigSize((wc_MlDsaKey*)ssl->hsKey); if (mSigSz <= 0) { ERROR_OUT(ALGO_ID_E, exit_scv); } args->sigLen = (word32)mSigSz; } #endif /* WOLFSSL_HAVE_MLDSA && !WOLFSSL_MLDSA_NO_SIGN */ #if defined(WOLFSSL_HAVE_SLHDSA) if (ssl->hsType == DYNAMIC_TYPE_SLHDSA) { int slhSigSz = wc_SlhDsaKey_SigSize((SlhDsaKey*)ssl->hsKey); if (slhSigSz <= 0) { ERROR_OUT(ALGO_ID_E, exit_scv); } args->sigLen = (word32)slhSigSz; } #endif /* WOLFSSL_HAVE_SLHDSA */ #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { #ifndef NO_RSA if (ssl->hsAltType == DYNAMIC_TYPE_RSA) { /* build encoded signature buffer */ XFREE(rsaSigBuf->buffer, ssl->heap, DYNAMIC_TYPE_SIGNATURE); rsaSigBuf->length = WC_MAX_DIGEST_SIZE; rsaSigBuf->buffer = (byte*)XMALLOC(rsaSigBuf->length, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (rsaSigBuf->buffer == NULL) { ERROR_OUT(MEMORY_E, exit_scv); } ret = CreateRSAEncodedSig(rsaSigBuf->buffer, args->altSigData, args->altSigDataSz, args->altSigAlgo, ssl->options.hashAlgo); if (ret < 0) goto exit_scv; rsaSigBuf->length = ret; ret = 0; } #endif /* !NO_RSA */ #ifdef HAVE_ECC if (ssl->hsAltType == DYNAMIC_TYPE_ECC) { ret = CreateECCEncodedSig(args->altSigData, args->altSigDataSz, ssl->options.hashAlgo); if (ret < 0) goto exit_scv; args->altSigDataSz = (word16)ret; ret = 0; } #endif /* HAVE_ECC */ } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_DO; } /* case TLS_ASYNC_BUILD */ FALL_THROUGH; case TLS_ASYNC_DO: { byte* sigOut = args->verify + HASH_SIG_SIZE + VERIFY_HEADER; #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* As we have two signatures in the message, we store * the length of each before the actual signature. This * is necessary, as we could have two algorithms with * variable length signatures. */ sigOut += OPAQUE16_LEN; } #endif /* Only the per-algorithm signing branches below consume this. */ (void)sigOut; #ifdef HAVE_ECC if (ssl->hsType == DYNAMIC_TYPE_ECC) { #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) if (ssl->buffers.keyType == sm2_sa_algo) { ret = Sm2wSm3Sign(ssl, TLS13_SM2_SIG_ID, TLS13_SM2_SIG_ID_SZ, args->sigData, args->sigDataSz, sigOut, &args->sigLen, (ecc_key*)ssl->hsKey, NULL); } else #endif { ret = EccSign(ssl, args->sigData, args->sigDataSz, sigOut, &args->sigLen, (ecc_key*)ssl->hsKey, #ifdef HAVE_PK_CALLBACKS ssl->buffers.key #else NULL #endif ); } args->length = args->sigLen; } #endif /* HAVE_ECC */ #ifdef HAVE_ED25519 if (ssl->hsType == DYNAMIC_TYPE_ED25519) { ret = Ed25519Sign(ssl, args->sigData, args->sigDataSz, sigOut, &args->sigLen, (ed25519_key*)ssl->hsKey, #ifdef HAVE_PK_CALLBACKS ssl->buffers.key #else NULL #endif ); args->length = args->sigLen; } #endif #ifdef HAVE_ED448 if (ssl->hsType == DYNAMIC_TYPE_ED448) { ret = Ed448Sign(ssl, args->sigData, args->sigDataSz, sigOut, &args->sigLen, (ed448_key*)ssl->hsKey, #ifdef HAVE_PK_CALLBACKS ssl->buffers.key #else NULL #endif ); args->length = args->sigLen; } #endif #if defined(HAVE_FALCON) if (ssl->hsType == DYNAMIC_TYPE_FALCON) { ret = wc_falcon_sign_msg(args->sigData, args->sigDataSz, sigOut, &args->sigLen, (falcon_key*)ssl->hsKey, ssl->rng); args->length = args->sigLen; } #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) if (ssl->hsType == DYNAMIC_TYPE_MLDSA) { ret = wc_MlDsaKey_SignCtx((wc_MlDsaKey*)ssl->hsKey, NULL, 0, sigOut, &args->sigLen, args->sigData, args->sigDataSz, ssl->rng); args->length = args->sigLen; } #endif /* WOLFSSL_HAVE_MLDSA */ #if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) if (ssl->hsType == DYNAMIC_TYPE_SLHDSA) { /* The FIPS wrapper for wc_SlhDsaKey_Sign gates on the per-thread * privateKeyReadEnable flag (unlike ML-DSA sign), returning * FIPS_PRIVATE_KEY_LOCKED_E when the key is locked. Bracket the * sign so it can read the SLH-DSA private key. */ PRIVATE_KEY_UNLOCK(); ret = wc_SlhDsaKey_Sign((SlhDsaKey*)ssl->hsKey, NULL, 0, args->sigData, args->sigDataSz, sigOut, &args->sigLen, ssl->rng); PRIVATE_KEY_LOCK(); args->length = args->sigLen; } #endif /* WOLFSSL_HAVE_SLHDSA */ #if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \ !defined(WOLFSSL_RSA_VERIFY_ONLY) if (ssl->hsType == DYNAMIC_TYPE_RSA) { args->toSign = rsaSigBuf->buffer; args->toSignSz = (word32)rsaSigBuf->length; #if defined(HAVE_PK_CALLBACKS) && \ defined(TLS13_RSA_PSS_SIGN_CB_NO_PREHASH) /* Pass full data to sign (args->sigData), not hash of */ if (ssl->ctx->RsaPssSignCb) { args->toSign = args->sigData; args->toSignSz = args->sigDataSz; } #endif ret = RsaSign(ssl, (const byte*)args->toSign, args->toSignSz, sigOut, &args->sigLen, args->sigAlgo, ssl->options.hashAlgo, (RsaKey*)ssl->hsKey, ssl->buffers.key); if (ret == 0) { args->length = args->sigLen; XMEMCPY(args->sigData, sigOut, args->sigLen); } } #endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY */ /* Check for error */ if (ret != 0) { goto exit_scv; } #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* Add signature length for the first signature. */ c16toa((word16)args->sigLen, sigOut - OPAQUE16_LEN); args->length += OPAQUE16_LEN; /* Advance our pointer to where we store the alt signature. * We also add additional space for the length field of the * second signature. */ sigOut += args->sigLen + OPAQUE16_LEN; /* Generate the alternative signature */ #ifdef HAVE_ECC if (ssl->hsAltType == DYNAMIC_TYPE_ECC) { ret = EccSign(ssl, args->altSigData, args->altSigDataSz, sigOut, &args->altSigLen, (ecc_key*)ssl->hsAltKey, #ifdef HAVE_PK_CALLBACKS ssl->buffers.altKey #else NULL #endif ); } #endif /* HAVE_ECC */ #if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \ !defined(WOLFSSL_RSA_VERIFY_ONLY) if (ssl->hsAltType == DYNAMIC_TYPE_RSA) { args->toSign = rsaSigBuf->buffer; args->toSignSz = (word32)rsaSigBuf->length; #if defined(HAVE_PK_CALLBACKS) && \ defined(TLS13_RSA_PSS_SIGN_CB_NO_PREHASH) /* Pass full data to sign (args->altSigData), not hash of */ if (ssl->ctx->RsaPssSignCb) { args->toSign = args->altSigData; args->toSignSz = (word32)args->altSigDataSz; } #endif ret = RsaSign(ssl, (const byte*)args->toSign, args->toSignSz, sigOut, &args->altSigLen, args->altSigAlgo, ssl->options.hashAlgo, (RsaKey*)ssl->hsAltKey, ssl->buffers.altKey); if (ret == 0) { XMEMCPY(args->altSigData, sigOut, args->altSigLen); } } #endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY */ #if defined(HAVE_FALCON) if (ssl->hsAltType == DYNAMIC_TYPE_FALCON) { ret = wc_falcon_sign_msg(args->altSigData, args->altSigDataSz, sigOut, &args->altSigLen, (falcon_key*)ssl->hsAltKey, ssl->rng); } #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) if (ssl->hsAltType == DYNAMIC_TYPE_MLDSA) { ret = wc_MlDsaKey_SignCtx((wc_MlDsaKey*)ssl->hsAltKey, NULL, 0, sigOut, &args->altSigLen, args->altSigData, args->altSigDataSz, ssl->rng); } #endif /* WOLFSSL_HAVE_MLDSA */ /* Check for error */ if (ret != 0) { goto exit_scv; } /* Add signature length for the alternative signature. */ c16toa((word16)args->altSigLen, sigOut - OPAQUE16_LEN); /* Add length of the alt sig to the total length */ args->length += args->altSigLen + OPAQUE16_LEN; } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* The TLS 1.3 CertificateVerify signature is length-prefixed with a * 2-byte field (opaque signature<0..2^16-1>), so the body - a single * signature, or the combined native + alternative signature with * WOLFSSL_DUAL_ALG_CERTS - cannot exceed 65535 bytes. Reject rather * than let c16toa truncate it into a malformed message. args->length * is word32 so the dual-alg accumulation above cannot wrap before * this check. */ if (args->length > WOLFSSL_MAX_16BIT) { ERROR_OUT(BUFFER_E, exit_scv); } /* Add signature length. */ c16toa((word16)args->length, args->verify + HASH_SIG_SIZE); /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_VERIFY; } /* case TLS_ASYNC_DO */ FALL_THROUGH; case TLS_ASYNC_VERIFY: { #ifndef NO_RSA if (ssl->hsType == DYNAMIC_TYPE_RSA) { /* check for signature faults */ ret = VerifyRsaSign(ssl, args->sigData, args->sigLen, rsaSigBuf->buffer, (word32)rsaSigBuf->length, args->sigAlgo, ssl->options.hashAlgo, (RsaKey*)ssl->hsKey, ssl->buffers.key); } #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH && ssl->hsAltType == DYNAMIC_TYPE_RSA) { /* check for signature faults */ ret = VerifyRsaSign(ssl, args->altSigData, args->altSigLen, rsaSigBuf->buffer, (word32)rsaSigBuf->length, args->altSigAlgo, ssl->options.hashAlgo, (RsaKey*)ssl->hsAltKey, ssl->buffers.altKey); } #endif /* WOLFSSL_DUAL_ALG_CERTS */ #endif /* !NO_RSA */ #if defined(HAVE_ECC) && defined(WOLFSSL_CHECK_SIG_FAULTS) if (ssl->hsType == DYNAMIC_TYPE_ECC) { byte* sigOut = args->verify + HASH_SIG_SIZE + VERIFY_HEADER; #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* Add our length offset. */ sigOut += OPAQUE16_LEN; } #endif #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) if (ssl->buffers.keyType == sm2_sa_algo) { ret = Sm2wSm3Verify(ssl, TLS13_SM2_SIG_ID, TLS13_SM2_SIG_ID_SZ, sigOut, args->sigLen, args->sigData, args->sigDataSz, (ecc_key*)ssl->hsKey, NULL); } else #endif { #ifdef HAVE_PK_CALLBACKS buffer tmp; /* Private key may be held by the PK callback. */ tmp.length = ssl->buffers.key ? ssl->buffers.key->length : 0; tmp.buffer = ssl->buffers.key ? ssl->buffers.key->buffer : NULL; #endif ret = EccVerify(ssl, sigOut, args->sigLen, args->sigData, args->sigDataSz, (ecc_key*)ssl->hsKey, #ifdef HAVE_PK_CALLBACKS &tmp #else NULL #endif ); } } #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH && ssl->hsAltType == DYNAMIC_TYPE_ECC) { /* check for signature faults */ byte* sigOut = args->verify + HASH_SIG_SIZE + VERIFY_HEADER + args->sigLen + OPAQUE16_LEN + OPAQUE16_LEN; #ifdef HAVE_PK_CALLBACKS buffer tmp; /* Private key may be held by the PK callback. */ tmp.length = ssl->buffers.altKey ? ssl->buffers.altKey->length : 0; tmp.buffer = ssl->buffers.altKey ? ssl->buffers.altKey->buffer : NULL; #endif ret = EccVerify(ssl, sigOut, args->altSigLen, args->altSigData, args->altSigDataSz, (ecc_key*)ssl->hsAltKey, #ifdef HAVE_PK_CALLBACKS &tmp #else NULL #endif ); } #endif /* WOLFSSL_DUAL_ALG_CERTS */ #endif /* HAVE_ECC && WOLFSSL_CHECK_SIG_FAULTS */ /* Check for error */ if (ret != 0) { goto exit_scv; } /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_FINALIZE; } /* case TLS_ASYNC_VERIFY */ FALL_THROUGH; case TLS_ASYNC_FINALIZE: { #ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY if (ssl->buffers.certVerifyMsg.buffer != NULL) { /* Streaming path: the assembled body sits in certVerifyMsg; * record its final length (exact even for variable-length * signatures). Per-fragment record/handshake headers are added * in TLS_ASYNC_END. */ ssl->buffers.certVerifyMsg.length = (word32)args->length + HASH_SIG_SIZE + VERIFY_HEADER; } #endif /* WOLFSSL_TLS13_STREAM_CERT_VERIFY */ /* In-place path: put the record and handshake headers on now. * args->output is NULL only on the streaming path. */ if (args->output != NULL) { AddTls13Headers(args->output, args->length + HASH_SIG_SIZE + VERIFY_HEADER, certificate_verify, ssl); args->sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + args->length + HASH_SIG_SIZE + VERIFY_HEADER; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) args->sendSz += recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA; #endif /* WOLFSSL_DTLS13 */ } /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_END; } /* case TLS_ASYNC_FINALIZE */ FALL_THROUGH; case TLS_ASYNC_END: { /* Body of the handshake message: [sigAlg(2) | sigLen(2) | sig]. In * the in-place path it sits at args->verify in the output buffer; * in the streaming path it sits in ssl->buffers.certVerifyMsg. */ word32 msgSz; word32 maxFrag = (word32)wolfssl_local_GetMaxPlaintextSize(ssl); byte* output; word32 fragSz; word32 i; int recSz; int thisSendSz; #ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY if (ssl->buffers.certVerifyMsg.buffer != NULL) { /* Streamed send: the assembled body lives in * ssl->buffers.certVerifyMsg and the send cursor in * ssl->fragOffset - both connection-level - so a non-blocking * WANT_WRITE resumes here (via TLS_ASYNC_END) without * recomputing the signature. Emit one encrypted record per * iteration and flush it, so the output buffer never holds more * than a single fragment. Only the first record carries the * handshake header; BuildTls13Message hashes each fragment's * plaintext in order, keeping the transcript hash correct. The * cursor is advanced before the flush because the record is * already committed to the output buffer; the cursor tracks * message-body -> record progress, not bytes on the wire, so a * WANT_WRITE resumes on the next fragment. Advancing after the * flush would rebuild and double-send the fragment on resume. * This is not merely a convention: the accept/connect loop * flushes pending output and returns WANT_WRITE without * re-entering this function until the output buffer drains, so * the committed record is never skipped or overwritten. * certVerifyMsg is released on completion or error at exit_scv; * it is kept across WANT_WRITE for resume. */ msgSz = ssl->buffers.certVerifyMsg.length; if (maxFrag <= HANDSHAKE_HEADER_SZ) { ERROR_OUT(BUFFER_E, exit_scv); } while (ssl->fragOffset < msgSz && ret == 0) { if (ssl->fragOffset == 0) { fragSz = maxFrag - HANDSHAKE_HEADER_SZ; if (fragSz > msgSz) fragSz = msgSz; i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; thisSendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + (int)fragSz + MAX_MSG_EXTRA; } else { fragSz = msgSz - ssl->fragOffset; if (fragSz > maxFrag) fragSz = maxFrag; i = RECORD_HEADER_SZ; thisSendSz = RECORD_HEADER_SZ + (int)fragSz + MAX_MSG_EXTRA; } if ((ret = CheckAvailableSize(ssl, thisSendSz)) != 0) { goto exit_scv; } output = GetOutputBuffer(ssl); if (ssl->fragOffset == 0) { AddTls13FragHeaders(output, fragSz, 0, msgSz, certificate_verify, ssl); } else { AddTls13RecordHeader(output, fragSz, handshake, ssl); } XMEMCPY(output + i, ssl->buffers.certVerifyMsg.buffer + ssl->fragOffset, fragSz); /* This message is always encrypted. */ recSz = BuildTls13Message(ssl, output, thisSendSz, output + RECORD_HEADER_SZ, (int)(i - RECORD_HEADER_SZ + fragSz), handshake, 1, 0, 0); if (recSz < 0) { ret = recSz; goto exit_scv; } #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) /* Trace the logical CertificateVerify once (first fragment), * matching the single-record path. */ if (ssl->fragOffset == 0) { if (ssl->hsInfoOn) AddPacketName(ssl, "CertificateVerify"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "CertificateVerify", handshake, output, recSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) goto exit_scv; } } #endif ssl->buffers.outputBuffer.length += (word32)recSz; ssl->fragOffset += fragSz; /* Flush every fragment unconditionally - unlike the * in-place path this cannot honor ssl->options.groupMessages * (batch with later handshake messages), because streaming * exists precisely to keep the output buffer bounded to a * single fragment. */ ret = SendBuffered(ssl); } ssl->options.buildingMsg = 0; break; } #endif /* WOLFSSL_TLS13_STREAM_CERT_VERIFY */ /* In-place path: the whole message is in args->output. */ msgSz = (word32)args->length + HASH_SIG_SIZE + VERIFY_HEADER; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ssl->options.buildingMsg = 0; /* Dtls13HandshakeSend takes word16 output/send sizes, so the * whole assembled CertificateVerify (signature plus framing) * must fit in 16 bits. Every current SLH-DSA parameter set * stays well under this (256f, the largest, is ~50KB), but * guard the casts explicitly - mirroring the args->length check * on the TLS path - so a future larger signature fails loudly * instead of being silently truncated into a malformed record. * outputSz is the reserved capacity and is >= sendSz, so it * bounds both casts. */ if (args->outputSz > WOLFSSL_MAX_16BIT) { ERROR_OUT(BUFFER_E, exit_scv); } ret = Dtls13HandshakeSend(ssl, args->output, (word16)args->outputSz, (word16)args->sendSz, certificate_verify, 1); if (ret != 0) goto exit_scv; break; } #endif /* WOLFSSL_DTLS13 */ if (HANDSHAKE_HEADER_SZ + msgSz <= maxFrag) { /* Fits in a single record: the common path used by RSA, ECC, * EdDSA and ML-DSA is left byte-for-byte unchanged. */ /* Always encrypted. A record AEAD pend propagates through * exit_scv (args kept) and the retry resumes the build. */ ret = BuildTls13Message(ssl, args->output, (int)args->outputSz, args->output + RECORD_HEADER_SZ, args->sendSz - RECORD_HEADER_SZ, handshake, 1, 0, TLS13_HS_ASYNC_OKAY); if (ret < 0) { goto exit_scv; } else { args->sendSz = ret; ret = 0; } #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "CertificateVerify"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "CertificateVerify", handshake, args->output, args->sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) goto exit_scv; } #endif ssl->buffers.outputBuffer.length += (word32)args->sendSz; } else { /* Signature does not fit in a single TLS record (e.g. SLH-DSA, * whose signatures range up to ~50KB). Fragment the handshake * message across multiple encrypted records. Only the first * fragment carries the 4-byte handshake header; the transcript * hash is maintained correctly because BuildTls13Message hashes * each fragment's plaintext in order. * * The fragmentation cursor lives in args (frag/fragOffset/ * fragActive) so that a WC_PENDING_E from the record AEAD under * WOLFSSL_ASYNC_CRYPT resumes on the same fragment rather than * restarting at offset 0 and re-emitting committed records. */ if (maxFrag <= HANDSHAKE_HEADER_SZ) { ERROR_OUT(BUFFER_E, exit_scv); } /* Copy the assembled body out of the output buffer once, before * we begin overwriting it with per-record data. args->frag is * preserved across async resumes and freed by FreeScv13Args. */ if (args->frag == NULL) { args->frag = (byte*)XMALLOC(msgSz, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (args->frag == NULL) { ERROR_OUT(MEMORY_E, exit_scv); } XMEMCPY(args->frag, args->verify, msgSz); args->fragOffset = 0; args->fragActive = 0; } while (args->fragOffset < msgSz && ret == 0) { if (args->fragOffset == 0) { fragSz = maxFrag - HANDSHAKE_HEADER_SZ; if (fragSz > msgSz) fragSz = msgSz; thisSendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + (int)fragSz + MAX_MSG_EXTRA; } else { fragSz = msgSz - args->fragOffset; if (fragSz > maxFrag) fragSz = maxFrag; thisSendSz = RECORD_HEADER_SZ + (int)fragSz + MAX_MSG_EXTRA; } if ((ret = CheckAvailableSize(ssl, thisSendSz)) != 0) { goto exit_scv; } output = GetOutputBuffer(ssl); i = RECORD_HEADER_SZ; if (args->fragOffset == 0) i += HANDSHAKE_HEADER_SZ; /* Lay out this fragment's record header and plaintext once. * On an async resume of a pending fragment they are already * in place (outputBuffer.length was not advanced), so skip * straight to re-driving BuildTls13Message. */ if (!args->fragActive) { if (args->fragOffset == 0) { AddTls13FragHeaders(output, fragSz, 0, msgSz, certificate_verify, ssl); } else { AddTls13RecordHeader(output, fragSz, handshake, ssl); } XMEMCPY(output + i, args->frag + args->fragOffset, fragSz); args->fragActive = 1; } i += fragSz; /* This message is always encrypted. */ recSz = BuildTls13Message(ssl, output, thisSendSz, output + RECORD_HEADER_SZ, (int)(i - RECORD_HEADER_SZ), handshake, 1, 0, 0); if (recSz < 0) { /* WC_PENDING_E leaves frag/fragOffset/fragActive intact * for resume; real errors are cleaned up by * FreeScv13Args at exit_scv. */ ret = recSz; goto exit_scv; } #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) /* Trace the logical CertificateVerify once (on the first * fragment), matching the single-record path rather than * emitting one entry per record. */ if (args->fragOffset == 0) { if (ssl->hsInfoOn) AddPacketName(ssl, "CertificateVerify"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "CertificateVerify", handshake, output, recSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) goto exit_scv; } } #endif ssl->buffers.outputBuffer.length += (word32)recSz; args->fragOffset += fragSz; args->fragActive = 0; } } ssl->options.buildingMsg = 0; if (!ssl->options.groupMessages) ret = SendBuffered(ssl); break; } default: ret = INPUT_CASE_ERROR; } /* switch(ssl->options.asyncState) */ exit_scv: #ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY /* A streamed CertificateVerify keeps its assembled body across a * non-blocking WANT_WRITE so the send resumes without recomputing the * signature; release it on completion or on any real error. This path is * mutually exclusive with WOLFSSL_ASYNC_CRYPT (see the feature guard in * internal.h), so ret is never WC_PENDING_E here and the buffer needs no * retention across an async resume. */ if (ret != WC_NO_ERR_TRACE(WANT_WRITE) && ssl->buffers.certVerifyMsg.buffer != NULL) { XFREE(ssl->buffers.certVerifyMsg.buffer, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); ssl->buffers.certVerifyMsg.buffer = NULL; ssl->buffers.certVerifyMsg.length = 0; ssl->fragOffset = 0; } #endif /* WOLFSSL_TLS13_STREAM_CERT_VERIFY */ #ifdef WOLFSSL_BLIND_PRIVATE_KEY if (ret == 0) { ret = wolfssl_priv_der_blind(ssl->rng, ssl->buffers.key, &ssl->buffers.keyMask); } else { wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask); } #endif WOLFSSL_LEAVE("SendTls13CertificateVerify", ret); WOLFSSL_END(WC_FUNC_CERTIFICATE_VERIFY_SEND); #ifdef WOLFSSL_ASYNC_CRYPT /* Handle async operation */ if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { return ret; } #endif /* WOLFSSL_ASYNC_CRYPT */ /* Final cleanup */ FreeScv13Args(ssl, args); FreeKeyExchange(ssl); #ifdef WOLFSSL_ASYNC_IO /* Cleanup async */ FreeAsyncCtx(ssl, 0); #endif if (ret != 0) { WOLFSSL_ERROR_VERBOSE(ret); } return ret; } #endif #endif /* !NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER */ #if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH) /* handle processing TLS v1.3 certificate (11) */ /* Parse and handle a TLS v1.3 Certificate message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of Certificate. * On exit, the index of byte after the Certificate message. * totalSz The length of the current handshake message. * returns 0 on success and otherwise failure. */ static int DoTls13Certificate(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 totalSz) { int ret = 0; WOLFSSL_START(WC_FUNC_CERTIFICATE_DO); WOLFSSL_ENTER("DoTls13Certificate"); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && ssl->options.handShakeDone) { /* certificate needs some special care after the handshake */ ret = Dtls13RtxProcessingCertificate( ssl, input + *inOutIdx, totalSz); } #endif /* WOLFSSL_DTLS13 */ if (ret == 0) ret = ProcessPeerCerts(ssl, input, inOutIdx, totalSz); if (ret == 0) { #if !defined(NO_WOLFSSL_CLIENT) if (ssl->options.side == WOLFSSL_CLIENT_END) ssl->options.serverState = SERVER_CERT_COMPLETE; #endif #if !defined(NO_WOLFSSL_SERVER) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) if (ssl->options.side == WOLFSSL_SERVER_END && ssl->options.handShakeState == HANDSHAKE_DONE) { /* reset handshake states */ ssl->options.serverState = SERVER_FINISHED_COMPLETE; ssl->options.acceptState = TICKET_SENT; ssl->options.handShakeState = SERVER_FINISHED_COMPLETE; } #endif } WOLFSSL_LEAVE("DoTls13Certificate", ret); WOLFSSL_END(WC_FUNC_CERTIFICATE_DO); return ret; } #endif #if (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_ED448) || defined(HAVE_FALCON) || \ defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA)) && \ !defined(NO_CERTS) typedef struct Dcv13Args { byte* output; /* not allocated */ word32 sendSz; word16 sz; word32 sigSz; word32 idx; word32 begin; byte* sigData; word16 sigDataSz; #ifdef WOLFSSL_DUAL_ALG_CERTS byte altSigAlgo; byte* altSigData; word32 altSigDataSz; word32 altSignatureSz; byte altPeerAuthGood; #endif } Dcv13Args; static void FreeDcv13Args(WOLFSSL* ssl, void* pArgs) { Dcv13Args* args = (Dcv13Args*)pArgs; if (args && args->sigData != NULL) { XFREE(args->sigData, ssl->heap, DYNAMIC_TYPE_SIGNATURE); args->sigData = NULL; } #ifdef WOLFSSL_DUAL_ALG_CERTS if (args && args->altSigData != NULL) { XFREE(args->altSigData, ssl->heap, DYNAMIC_TYPE_SIGNATURE); args->altSigData = NULL; } #endif (void)ssl; } #ifdef WOLFSSL_DUAL_ALG_CERTS #ifndef NO_RSA /* ssl->peerCert->sapkiDer is the alternative public key. Hopefully it is a * RSA public key. Convert it into a usable public key. */ static int decodeRsaKey(WOLFSSL* ssl) { int keyRet; word32 tmpIdx = 0; if (ssl->peerRsaKeyPresent) return INVALID_PARAMETER; keyRet = AllocKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey); if (keyRet != 0) return PEER_KEY_ERROR; ssl->peerRsaKeyPresent = 1; keyRet = wc_RsaPublicKeyDecode(ssl->peerCert.sapkiDer, &tmpIdx, ssl->peerRsaKey, ssl->peerCert.sapkiLen); if (keyRet != 0) return PEER_KEY_ERROR; return 0; } #endif /* !NO_RSA */ #ifdef HAVE_ECC /* ssl->peerCert->sapkiDer is the alternative public key. Hopefully it is a * ECC public key. Convert it into a usable public key. */ static int decodeEccKey(WOLFSSL* ssl) { int keyRet; word32 tmpIdx = 0; if (ssl->peerEccDsaKeyPresent) return INVALID_PARAMETER; keyRet = AllocKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccDsaKey); if (keyRet != 0) return PEER_KEY_ERROR; ssl->peerEccDsaKeyPresent = 1; keyRet = wc_EccPublicKeyDecode(ssl->peerCert.sapkiDer, &tmpIdx, ssl->peerEccDsaKey, ssl->peerCert.sapkiLen); if (keyRet != 0) return PEER_KEY_ERROR; return 0; } #endif /* HAVE_ECC */ #ifdef WOLFSSL_HAVE_MLDSA /* ssl->peerCert->sapkiDer is the alternative public key. Hopefully it is a * ML-DSA public key. Convert it into a usable public key. */ static int decodeMlDsaKey(WOLFSSL* ssl, int level) { int keyRet; word32 tmpIdx = 0; if (ssl->peerMlDsaKeyPresent) return INVALID_PARAMETER; keyRet = AllocKey(ssl, DYNAMIC_TYPE_MLDSA, (void**)&ssl->peerMlDsaKey); if (keyRet != 0) return PEER_KEY_ERROR; ssl->peerMlDsaKeyPresent = 1; keyRet = wc_MlDsaKey_SetParams(ssl->peerMlDsaKey, level); if (keyRet != 0) return PEER_KEY_ERROR; keyRet = wc_MlDsaKey_PublicKeyDecode(ssl->peerMlDsaKey, ssl->peerCert.sapkiDer, ssl->peerCert.sapkiLen, &tmpIdx); if (keyRet != 0) return PEER_KEY_ERROR; return 0; } #endif /* WOLFSSL_HAVE_MLDSA */ #ifdef HAVE_FALCON /* ssl->peerCert->sapkiDer is the alternative public key. Hopefully it is a * falcon public key. Convert it into a usable public key. */ static int decodeFalconKey(WOLFSSL* ssl, int level) { int keyRet; word32 tmpIdx = 0; if (ssl->peerFalconKeyPresent) return INVALID_PARAMETER; keyRet = AllocKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey); if (keyRet != 0) return PEER_KEY_ERROR; ssl->peerFalconKeyPresent = 1; keyRet = wc_falcon_set_level(ssl->peerFalconKey, level); if (keyRet != 0) return PEER_KEY_ERROR; keyRet = wc_Falcon_PublicKeyDecode(ssl->peerCert.sapkiDer, &tmpIdx, ssl->peerFalconKey, ssl->peerCert.sapkiLen); if (keyRet != 0) return PEER_KEY_ERROR; return 0; } #endif /* HAVE_FALCON */ #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* handle processing TLS v1.3 certificate_verify (15) */ /* Parse and handle a TLS v1.3 CertificateVerify message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of * CertificateVerify. * On exit, the index of byte after the CertificateVerify message. * totalSz The length of the current handshake message. * returns 0 on success and otherwise failure. */ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 totalSz) { int ret = 0; byte* sig = NULL; #ifndef NO_RSA /* Use this as a temporary buffer for RSA signature verification. */ buffer* rsaSigBuf = &ssl->buffers.sig; #endif #ifdef WOLFSSL_ASYNC_CRYPT Dcv13Args* args = NULL; WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args); #else Dcv13Args args[1]; #endif WOLFSSL_START(WC_FUNC_CERTIFICATE_VERIFY_DO); WOLFSSL_ENTER("DoTls13CertificateVerify"); #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13CertificateVerify(ssl, input, inOutIdx, totalSz); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { goto exit_dcv; } ret = 0; #endif #ifdef WOLFSSL_ASYNC_CRYPT if (ssl->async == NULL) { ssl->async = (struct WOLFSSL_ASYNC*) XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap, DYNAMIC_TYPE_ASYNC); if (ssl->async == NULL) ERROR_OUT(MEMORY_E, exit_dcv); } args = (Dcv13Args*)ssl->async->args; ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* Check for error */ if (ret < 0) goto exit_dcv; } else #endif { /* Reset state */ ret = 0; ssl->options.asyncState = TLS_ASYNC_BEGIN; XMEMSET(args, 0, sizeof(Dcv13Args)); ssl->options.peerHashAlgo = sha_mac; ssl->options.peerSigAlgo = anonymous_sa_algo; args->idx = *inOutIdx; args->begin = *inOutIdx; #ifdef WOLFSSL_ASYNC_CRYPT ssl->async->freeArgs = FreeDcv13Args; #endif } switch(ssl->options.asyncState) { case TLS_ASYNC_BEGIN: { #ifdef WOLFSSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName(ssl, "CertificateVerify"); if (ssl->toInfoOn) AddLateName("CertificateVerify", &ssl->timeoutInfo); #endif /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_BUILD; } /* case TLS_ASYNC_BEGIN */ FALL_THROUGH; case TLS_ASYNC_BUILD: { int validSigAlgo; const Suites* suites = WOLFSSL_SUITES(ssl); word16 i; /* Signature algorithm. */ if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN > totalSz) { ERROR_OUT(BUFFER_ERROR, exit_dcv); } #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->peerSigSpec == NULL) { /* The peer did not respond. We didn't send CKS or they don't * support it. Either way, we do not need to handle dual * key/sig case. */ ssl->sigSpec = NULL; ssl->sigSpecSz = 0; } /* If no CKS extension or either native or alternative, then just * get a normal sigalgo. But if BOTH, then get the native and alt * sig algos. */ if (ssl->sigSpec == NULL || *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_NATIVE || *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE) { #endif /* WOLFSSL_DUAL_ALG_CERTS */ validSigAlgo = 0; for (i = 0; i < suites->hashSigAlgoSz; i += 2) { if ((suites->hashSigAlgo[i + 0] == input[args->idx + 0]) && (suites->hashSigAlgo[i + 1] == input[args->idx + 1])) { validSigAlgo = 1; break; } } if (!validSigAlgo) { ERROR_OUT(INVALID_PARAMETER, exit_dcv); } ret = DecodeTls13SigAlg(input + args->idx, &ssl->options.peerHashAlgo, &ssl->options.peerSigAlgo); #ifdef WOLFSSL_DUAL_ALG_CERTS } else { ret = DecodeTls13HybridSigAlg(input + args->idx, &ssl->options.peerHashAlgo, &ssl->options.peerSigAlgo, &args->altSigAlgo); } #endif /* WOLFSSL_DUAL_ALG_CERTS */ if (ret < 0) goto exit_dcv; args->idx += OPAQUE16_LEN; /* Signature length. */ if ((args->idx - args->begin) + OPAQUE16_LEN > totalSz) { ERROR_OUT(BUFFER_ERROR, exit_dcv); } ato16(input + args->idx, &args->sz); args->idx += OPAQUE16_LEN; /* Signature data. */ if ((args->idx - args->begin) + args->sz > totalSz) { ERROR_OUT(BUFFER_ERROR, exit_dcv); } #ifdef WOLFSSL_DUAL_ALG_CERTS if ((ssl->sigSpec != NULL) && (*ssl->sigSpec != WOLFSSL_CKS_SIGSPEC_NATIVE)) { word16 sa; if (args->altSigAlgo == 0) sa = ssl->options.peerSigAlgo; else sa = args->altSigAlgo; switch(sa) { #ifndef NO_RSA case rsa_pss_sa_algo: ret = decodeRsaKey(ssl); break; #endif #ifdef HAVE_ECC case ecc_dsa_sa_algo: ret = decodeEccKey(ssl); break; #endif #ifdef WOLFSSL_HAVE_MLDSA case mldsa_44_sa_algo: ret = decodeMlDsaKey(ssl, WC_ML_DSA_44); break; case mldsa_65_sa_algo: ret = decodeMlDsaKey(ssl, WC_ML_DSA_65); break; case mldsa_87_sa_algo: ret = decodeMlDsaKey(ssl, WC_ML_DSA_87); break; #endif #ifdef WOLFSSL_HAVE_SLHDSA case slhdsa_sha2_128s_sa_algo: case slhdsa_sha2_128f_sa_algo: case slhdsa_sha2_192s_sa_algo: case slhdsa_sha2_192f_sa_algo: case slhdsa_sha2_256s_sa_algo: case slhdsa_sha2_256f_sa_algo: case slhdsa_shake_128s_sa_algo: case slhdsa_shake_128f_sa_algo: case slhdsa_shake_192s_sa_algo: case slhdsa_shake_192f_sa_algo: case slhdsa_shake_256s_sa_algo: case slhdsa_shake_256f_sa_algo: /* SLH-DSA is not supported as an alternative (dual-algorithm * / CKS) key. The alternative-signature verification block * in this function has no SLH-DSA case, so a decoded key * would never be verified and the handshake would always be * rejected at TLS_ASYNC_FINALIZE. Fail fast here instead of * allocating a key that can never authenticate the peer. */ ERROR_OUT(ALGO_ID_E, exit_dcv); #endif #ifdef HAVE_FALCON case falcon_level1_sa_algo: ret = decodeFalconKey(ssl, 1); break; case falcon_level5_sa_algo: ret = decodeFalconKey(ssl, 5); break; #endif default: ERROR_OUT(PEER_KEY_ERROR, exit_dcv); } if (ret != 0) ERROR_OUT(ret, exit_dcv); if (*ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_ALTERNATIVE) { /* Now swap in the alternative by removing the native. * sa contains the alternative signature type. */ #ifndef NO_RSA if (ssl->peerRsaKeyPresent && sa != rsa_pss_sa_algo) { FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey); ssl->peerRsaKeyPresent = 0; } #endif #ifdef HAVE_ECC else if (ssl->peerEccDsaKeyPresent && sa != ecc_dsa_sa_algo) { FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccDsaKey); ssl->peerEccDsaKeyPresent = 0; } #endif #ifdef WOLFSSL_HAVE_MLDSA else if (ssl->peerMlDsaKeyPresent && sa != mldsa_44_sa_algo && sa != mldsa_65_sa_algo && sa != mldsa_87_sa_algo) { FreeKey(ssl, DYNAMIC_TYPE_MLDSA, (void**)&ssl->peerMlDsaKey); ssl->peerMlDsaKeyPresent = 0; } #endif #ifdef WOLFSSL_HAVE_SLHDSA else if (ssl->peerSlhDsaKeyPresent && !IsSlhDsaSigAlgo(sa)) { FreeKey(ssl, DYNAMIC_TYPE_SLHDSA, (void**)&ssl->peerSlhDsaKey); ssl->peerSlhDsaKeyPresent = 0; } #endif #ifdef HAVE_FALCON else if (ssl->peerFalconKeyPresent && sa != falcon_level1_sa_algo && sa != falcon_level5_sa_algo) { FreeKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey); ssl->peerFalconKeyPresent = 0; } #endif else { ERROR_OUT(PEER_KEY_ERROR, exit_dcv); } } } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* Check for public key of required type. */ /* Assume invalid unless signature algo matches the key provided */ validSigAlgo = 0; #ifdef HAVE_ED25519 if (ssl->options.peerSigAlgo == ed25519_sa_algo) { WOLFSSL_MSG("Peer sent ED25519 sig"); validSigAlgo = (ssl->peerEd25519Key != NULL) && ssl->peerEd25519KeyPresent; } #endif #ifdef HAVE_ED448 if (ssl->options.peerSigAlgo == ed448_sa_algo) { WOLFSSL_MSG("Peer sent ED448 sig"); validSigAlgo = (ssl->peerEd448Key != NULL) && ssl->peerEd448KeyPresent; } #endif #ifdef HAVE_ECC if (ssl->options.peerSigAlgo == ecc_dsa_sa_algo) { WOLFSSL_MSG("Peer sent ECC sig"); validSigAlgo = (ssl->peerEccDsaKey != NULL) && ssl->peerEccDsaKeyPresent; } #endif #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) if (ssl->options.peerSigAlgo == sm2_sa_algo) { WOLFSSL_MSG("Peer sent SM2 sig"); validSigAlgo = (ssl->peerEccDsaKey != NULL) && ssl->peerEccDsaKeyPresent; } #endif #ifdef HAVE_FALCON if (ssl->options.peerSigAlgo == falcon_level1_sa_algo) { WOLFSSL_MSG("Peer sent Falcon Level 1 sig"); validSigAlgo = (ssl->peerFalconKey != NULL) && ssl->peerFalconKeyPresent; } if (ssl->options.peerSigAlgo == falcon_level5_sa_algo) { WOLFSSL_MSG("Peer sent Falcon Level 5 sig"); validSigAlgo = (ssl->peerFalconKey != NULL) && ssl->peerFalconKeyPresent; } #endif #ifdef WOLFSSL_HAVE_MLDSA if (ssl->options.peerSigAlgo == mldsa_44_sa_algo) { WOLFSSL_MSG("Peer sent ML-DSA Level 2 sig"); validSigAlgo = (ssl->peerMlDsaKey != NULL) && ssl->peerMlDsaKeyPresent; } if (ssl->options.peerSigAlgo == mldsa_65_sa_algo) { WOLFSSL_MSG("Peer sent ML-DSA Level 3 sig"); validSigAlgo = (ssl->peerMlDsaKey != NULL) && ssl->peerMlDsaKeyPresent; } if (ssl->options.peerSigAlgo == mldsa_87_sa_algo) { WOLFSSL_MSG("Peer sent ML-DSA Level 5 sig"); validSigAlgo = (ssl->peerMlDsaKey != NULL) && ssl->peerMlDsaKeyPresent; } #endif #ifdef WOLFSSL_HAVE_SLHDSA if (IsSlhDsaSigAlgo(ssl->options.peerSigAlgo)) { WOLFSSL_MSG("Peer sent SLH-DSA sig"); validSigAlgo = (ssl->peerSlhDsaKey != NULL) && ssl->peerSlhDsaKeyPresent; } #endif #ifndef NO_RSA if (ssl->options.peerSigAlgo == rsa_sa_algo) { WOLFSSL_MSG("Peer sent PKCS#1.5 algo - not valid TLS 1.3"); ERROR_OUT(INVALID_PARAMETER, exit_dcv); } if (ssl->options.peerSigAlgo == rsa_pss_sa_algo) { WOLFSSL_MSG("Peer sent RSA sig"); validSigAlgo = (ssl->peerRsaKey != NULL) && ssl->peerRsaKeyPresent; } #endif if (!validSigAlgo) { WOLFSSL_MSG("Sig algo doesn't correspond to certificate"); ERROR_OUT(SIG_VERIFY_E, exit_dcv); } args->sigSz = args->sz; #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* In case we received two signatures, both of them are encoded * with their size as 16-bit integeter prior in memory. Hence, * we can decode both lengths here now. */ word32 tmpIdx = args->idx; word16 tmpSz = 0; if (args->sz < OPAQUE16_LEN) { ERROR_OUT(BUFFER_ERROR, exit_dcv); } ato16(input + tmpIdx, &tmpSz); args->sigSz = tmpSz; tmpIdx += OPAQUE16_LEN + args->sigSz; if (tmpIdx - args->idx + OPAQUE16_LEN > args->sz) { ERROR_OUT(BUFFER_ERROR, exit_dcv); } ato16(input + tmpIdx, &tmpSz); args->altSignatureSz = tmpSz; if (args->sz != (args->sigSz + args->altSignatureSz + OPAQUE16_LEN + OPAQUE16_LEN)) { ERROR_OUT(BUFFER_ERROR, exit_dcv); } } #endif /* WOLFSSL_DUAL_ALG_CERTS */ #if !defined(NO_RSA) && defined(WC_RSA_PSS) /* In case we have to verify an RSA signature, we have to store the * signature in the 'rsaSigBuf' structure for further processing. */ if (ssl->peerRsaKey != NULL && ssl->peerRsaKeyPresent != 0) { word32 sigSz = args->sigSz; sig = input + args->idx; #ifdef WOLFSSL_DUAL_ALG_CERTS /* Check if our alternative signature was RSA */ if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { if (ssl->options.peerSigAlgo != rsa_pss_sa_algo) { /* We have to skip the first signature (length field * and signature itself) and the length field of the * alternative signature. */ sig += OPAQUE16_LEN + OPAQUE16_LEN + args->sigSz; sigSz = args->altSignatureSz; } else { /* We have to skip the length field */ sig += OPAQUE16_LEN; } } #endif rsaSigBuf->buffer = (byte*)XMALLOC(sigSz, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (rsaSigBuf->buffer == NULL) { ERROR_OUT(MEMORY_E, exit_dcv); } rsaSigBuf->length = sigSz; XMEMCPY(rsaSigBuf->buffer, sig, rsaSigBuf->length); } #endif /* !NO_RSA && WC_RSA_PSS */ args->sigData = (byte*)XMALLOC(MAX_SIG_DATA_SZ, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (args->sigData == NULL) { ERROR_OUT(MEMORY_E, exit_dcv); } ret = CreateSigData(ssl, args->sigData, &args->sigDataSz, 1); if (ret < 0) goto exit_dcv; #ifdef WOLFSSL_DUAL_ALG_CERTS if ((ssl->sigSpec != NULL) && (*ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH)) { args->altSigData = (byte*)XMALLOC(MAX_SIG_DATA_SZ, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (args->altSigData == NULL) { ERROR_OUT(MEMORY_E, exit_dcv); } XMEMCPY(args->altSigData, args->sigData, args->sigDataSz); args->altSigDataSz = args->sigDataSz; } #endif /* WOLFSSL_DUAL_ALG_CERTS */ #ifdef HAVE_ECC if ((ssl->options.peerSigAlgo == ecc_dsa_sa_algo) && (ssl->peerEccDsaKeyPresent)) { ret = CreateECCEncodedSig(args->sigData, args->sigDataSz, ssl->options.peerHashAlgo); if (ret < 0) goto exit_dcv; args->sigDataSz = (word16)ret; ret = 0; } #ifdef WOLFSSL_DUAL_ALG_CERTS if ((ssl->sigSpec != NULL) && (*ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) && (args->altSigAlgo == ecc_dsa_sa_algo) && (ssl->peerEccDsaKeyPresent)) { ret = CreateECCEncodedSig(args->altSigData, args->altSigDataSz, ssl->options.peerHashAlgo); if (ret < 0) goto exit_dcv; args->altSigDataSz = (word16)ret; ret = 0; } #endif /* WOLFSSL_DUAL_ALG_CERTS */ #endif /* HAVE_ECC */ /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_DO; } /* case TLS_ASYNC_BUILD */ FALL_THROUGH; case TLS_ASYNC_DO: { sig = input + args->idx; (void)sig; #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* As we have two signatures in the message, we stored * the length of each before the actual signature. This * is necessary, as we could have two algorithms with * variable length signatures. */ sig += OPAQUE16_LEN; } #endif #ifndef NO_RSA if ((ssl->options.peerSigAlgo == rsa_pss_sa_algo) && (ssl->peerRsaKey != NULL) && (ssl->peerRsaKeyPresent != 0)) { WOLFSSL_MSG("Doing RSA peer cert verify"); ret = RsaVerify(ssl, rsaSigBuf->buffer, (word32)rsaSigBuf->length, &args->output, ssl->options.peerSigAlgo, ssl->options.peerHashAlgo, ssl->peerRsaKey, #ifdef HAVE_PK_CALLBACKS &ssl->buffers.peerRsaKey #else NULL #endif ); if (ret >= 0) { args->sendSz = (word32)ret; ret = 0; } } #endif /* !NO_RSA */ #ifdef HAVE_ECC if ((ssl->options.peerSigAlgo == ecc_dsa_sa_algo) && ssl->peerEccDsaKeyPresent) { WOLFSSL_MSG("Doing ECC peer cert verify"); ret = EccVerify(ssl, sig, args->sigSz, args->sigData, args->sigDataSz, ssl->peerEccDsaKey, #ifdef HAVE_PK_CALLBACKS &ssl->buffers.peerEccDsaKey #else NULL #endif ); if (ret >= 0) { /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->options.peerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccDsaKey); ssl->peerEccDsaKeyPresent = 0; } } #endif /* HAVE_ECC */ #if defined(HAVE_ECC) && defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) if ((ssl->options.peerSigAlgo == sm2_sa_algo) && ssl->peerEccDsaKeyPresent) { WOLFSSL_MSG("Doing SM2/SM3 peer cert verify"); ret = Sm2wSm3Verify(ssl, TLS13_SM2_SIG_ID, TLS13_SM2_SIG_ID_SZ, sig, args->sigSz, args->sigData, args->sigDataSz, ssl->peerEccDsaKey, NULL); if (ret >= 0) { /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->options.peerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccDsaKey); ssl->peerEccDsaKeyPresent = 0; } } #endif #ifdef HAVE_ED25519 if ((ssl->options.peerSigAlgo == ed25519_sa_algo) && (ssl->peerEd25519KeyPresent)) { WOLFSSL_MSG("Doing ED25519 peer cert verify"); ret = Ed25519Verify(ssl, sig, args->sigSz, args->sigData, args->sigDataSz, ssl->peerEd25519Key, #ifdef HAVE_PK_CALLBACKS &ssl->buffers.peerEd25519Key #else NULL #endif ); if (ret >= 0) { /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->options.peerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_ED25519, (void**)&ssl->peerEd25519Key); ssl->peerEd25519KeyPresent = 0; } } #endif #ifdef HAVE_ED448 if ((ssl->options.peerSigAlgo == ed448_sa_algo) && (ssl->peerEd448KeyPresent)) { WOLFSSL_MSG("Doing ED448 peer cert verify"); ret = Ed448Verify(ssl, sig, args->sigSz, args->sigData, args->sigDataSz, ssl->peerEd448Key, #ifdef HAVE_PK_CALLBACKS &ssl->buffers.peerEd448Key #else NULL #endif ); if (ret >= 0) { /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->options.peerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_ED448, (void**)&ssl->peerEd448Key); ssl->peerEd448KeyPresent = 0; } } #endif #if defined(HAVE_FALCON) if (((ssl->options.peerSigAlgo == falcon_level1_sa_algo) || (ssl->options.peerSigAlgo == falcon_level5_sa_algo)) && (ssl->peerFalconKeyPresent)) { int res = 0; WOLFSSL_MSG("Doing Falcon peer cert verify"); ret = wc_falcon_verify_msg(sig, args->sigSz, args->sigData, args->sigDataSz, &res, ssl->peerFalconKey); if ((ret >= 0) && (res == 1)) { /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->options.peerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey); ssl->peerFalconKeyPresent = 0; } else if ((ret >= 0) && (res == 0)) { WOLFSSL_MSG("Falcon signature verification failed"); ret = SIG_VERIFY_E; } } #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_VERIFY) if (((ssl->options.peerSigAlgo == mldsa_44_sa_algo) || (ssl->options.peerSigAlgo == mldsa_65_sa_algo) || (ssl->options.peerSigAlgo == mldsa_87_sa_algo)) && (ssl->peerMlDsaKeyPresent)) { int res = 0; WOLFSSL_MSG("Doing ML-DSA peer cert verify"); ret = wc_MlDsaKey_VerifyCtx(ssl->peerMlDsaKey, sig, args->sigSz, NULL, 0, args->sigData, args->sigDataSz, &res); if ((ret >= 0) && (res == 1)) { /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->options.peerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_MLDSA, (void**)&ssl->peerMlDsaKey); ssl->peerMlDsaKeyPresent = 0; } else if ((ret >= 0) && (res == 0)) { WOLFSSL_MSG("ML-DSA signature verification failed"); ret = SIG_VERIFY_E; } } #endif /* WOLFSSL_HAVE_MLDSA */ #if defined(WOLFSSL_HAVE_SLHDSA) if (IsSlhDsaSigAlgo(ssl->options.peerSigAlgo) && (ssl->peerSlhDsaKeyPresent)) { WOLFSSL_MSG("Doing SLH-DSA peer cert verify"); /* The advertised signature scheme must match the parameter set * of the peer's certificate key (RFC 8446 4.4.3). The key * params come from the certificate OID, so a mismatch means the * peer is over-claiming its security level; reject it. */ if ((ssl->peerSlhDsaKey->params == NULL) || (SlhDsaParamToType((int)ssl->peerSlhDsaKey->params->param) != ssl->options.peerSigAlgo)) { ERROR_OUT(SIG_VERIFY_E, exit_dcv); } /* wc_SlhDsaKey_Verify returns 0 for a valid signature. */ ret = wc_SlhDsaKey_Verify(ssl->peerSlhDsaKey, NULL, 0, args->sigData, args->sigDataSz, sig, args->sigSz); if (ret == 0) { /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->options.peerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_SLHDSA, (void**)&ssl->peerSlhDsaKey); ssl->peerSlhDsaKeyPresent = 0; } else { /* wc_SlhDsaKey_Verify already returns SIG_VERIFY_E on a * signature mismatch and propagates real errors verbatim * (WC_PENDING_E on the async crypto-callback path, MEMORY_E, * ...), so leave ret unchanged. Flattening everything to * SIG_VERIFY_E would break async resume and mask * diagnostics; the ML-DSA/Falcon blocks above likewise * preserve non-completion return codes. */ WOLFSSL_MSG("SLH-DSA signature verification failed"); } } #endif /* WOLFSSL_HAVE_SLHDSA */ /* Check for error */ if (ret != 0) { goto exit_dcv; } #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { /* Move forward to the alternative signature. */ sig += args->sigSz + OPAQUE16_LEN; /* Verify the alternative signature */ #ifndef NO_RSA if ((args->altSigAlgo == rsa_pss_sa_algo) && (ssl->peerRsaKey != NULL) && (ssl->peerRsaKeyPresent != 0)) { WOLFSSL_MSG("Doing RSA peer cert alt verify"); ret = RsaVerify(ssl, rsaSigBuf->buffer, (word32)rsaSigBuf->length, &args->output, args->altSigAlgo, ssl->options.peerHashAlgo, ssl->peerRsaKey, #ifdef HAVE_PK_CALLBACKS &ssl->buffers.peerRsaKey #else NULL #endif ); if (ret >= 0) { args->sendSz = ret; ret = 0; } } #endif /* !NO_RSA */ #ifdef HAVE_ECC if ((args->altSigAlgo == ecc_dsa_sa_algo) && (ssl->peerEccDsaKeyPresent)) { WOLFSSL_MSG("Doing ECC peer cert alt verify"); ret = EccVerify(ssl, sig, args->altSignatureSz, args->altSigData, args->altSigDataSz, ssl->peerEccDsaKey, #ifdef HAVE_PK_CALLBACKS &ssl->buffers.peerEccDsaKey #else NULL #endif ); if (ret >= 0) { /* CLIENT/SERVER: data verified with public key from * certificate. */ args->altPeerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccDsaKey); ssl->peerEccDsaKeyPresent = 0; } } #endif /* HAVE_ECC */ #if defined(HAVE_FALCON) if (((args->altSigAlgo == falcon_level1_sa_algo) || (args->altSigAlgo == falcon_level5_sa_algo)) && (ssl->peerFalconKeyPresent)) { int res = 0; WOLFSSL_MSG("Doing Falcon peer cert alt verify"); ret = wc_falcon_verify_msg(sig, args->altSignatureSz, args->altSigData, args->altSigDataSz, &res, ssl->peerFalconKey); if ((ret >= 0) && (res == 1)) { /* CLIENT/SERVER: data verified with public key from * certificate. */ args->altPeerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey); ssl->peerFalconKeyPresent = 0; } else if ((ret >= 0) && (res == 0)) { WOLFSSL_MSG("Falcon signature verification failed"); ret = SIG_VERIFY_E; } } #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_VERIFY) if (((args->altSigAlgo == mldsa_44_sa_algo) || (args->altSigAlgo == mldsa_65_sa_algo) || (args->altSigAlgo == mldsa_87_sa_algo)) && (ssl->peerMlDsaKeyPresent)) { int res = 0; WOLFSSL_MSG("Doing ML-DSA peer cert alt verify"); ret = wc_MlDsaKey_VerifyCtx(ssl->peerMlDsaKey, sig, args->altSignatureSz, NULL, 0, args->altSigData, args->altSigDataSz, &res); if ((ret >= 0) && (res == 1)) { /* CLIENT/SERVER: data verified with public key from * certificate. */ args->altPeerAuthGood = 1; FreeKey(ssl, DYNAMIC_TYPE_MLDSA, (void**)&ssl->peerMlDsaKey); ssl->peerMlDsaKeyPresent = 0; } else if ((ret >= 0) && (res == 0)) { WOLFSSL_MSG("ML-DSA signature verification failed"); ret = SIG_VERIFY_E; } } #endif /* WOLFSSL_HAVE_MLDSA */ /* Check for error */ if (ret != 0) { goto exit_dcv; } } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_VERIFY; } /* case TLS_ASYNC_DO */ FALL_THROUGH; case TLS_ASYNC_VERIFY: { #if !defined(NO_RSA) && defined(WC_RSA_PSS) if (ssl->peerRsaKey != NULL && ssl->peerRsaKeyPresent != 0) { int sigAlgo = ssl->options.peerSigAlgo; #ifdef WOLFSSL_DUAL_ALG_CERTS /* Check if our alternative signature was RSA */ if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH && ssl->options.peerSigAlgo != rsa_pss_sa_algo) { sigAlgo = args->altSigAlgo; } #endif ret = CheckRSASignature(ssl, sigAlgo, ssl->options.peerHashAlgo, args->output, args->sendSz); if (ret != 0) goto exit_dcv; /* CLIENT/SERVER: data verified with public key from * certificate. */ ssl->peerRsaKeyPresent = 0; FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey); #ifdef WOLFSSL_DUAL_ALG_CERTS /* Check if our alternative signature was RSA */ if (ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH && ssl->options.peerSigAlgo != rsa_pss_sa_algo) { args->altPeerAuthGood = 1; } else #endif ssl->options.peerAuthGood = 1; } #endif /* !NO_RSA && WC_RSA_PSS */ /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_FINALIZE; } /* case TLS_ASYNC_VERIFY */ FALL_THROUGH; case TLS_ASYNC_FINALIZE: { #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->options.peerAuthGood && ssl->sigSpec != NULL && *ssl->sigSpec == WOLFSSL_CKS_SIGSPEC_BOTH) { ssl->options.peerAuthGood = args->altPeerAuthGood; } #endif /* WOLFSSL_DUAL_ALG_CERTS */ ssl->options.havePeerVerify = 1; /* Set final index */ args->idx += args->sz; *inOutIdx = args->idx; /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_END; #if !defined(NO_WOLFSSL_CLIENT) if (ssl->options.side == WOLFSSL_CLIENT_END) ssl->options.serverState = SERVER_CERT_VERIFY_COMPLETE; #endif } /* case TLS_ASYNC_FINALIZE */ FALL_THROUGH; case TLS_ASYNC_END: { break; } default: ret = INPUT_CASE_ERROR; } /* switch(ssl->options.asyncState) */ exit_dcv: WOLFSSL_LEAVE("DoTls13CertificateVerify", ret); WOLFSSL_END(WC_FUNC_CERTIFICATE_VERIFY_DO); #ifdef WOLFSSL_ASYNC_CRYPT /* Handle async operation */ if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { /* Mark message as not received so it can process again */ ssl->msgsReceived.got_certificate_verify = 0; return ret; } else #endif /* WOLFSSL_ASYNC_CRYPT */ if (ret != 0) { WOLFSSL_ERROR_VERBOSE(ret); if (ret != WC_NO_ERR_TRACE(INVALID_PARAMETER)) { SendAlert(ssl, alert_fatal, decrypt_error); } } /* Final cleanup */ FreeDcv13Args(ssl, args); FreeKeyExchange(ssl); #ifdef WOLFSSL_ASYNC_IO /* Cleanup async */ FreeAsyncCtx(ssl, 0); #endif #ifdef WOLFSSL_ASYNC_CRYPT /* Replays skip the sanity check that re-sets got_certificate_verify; * restore on completion or Finished reports out-of-order. */ if (ret == 0 && ssl->msgsReceived.got_certificate_verify == 0) { ssl->msgsReceived.got_certificate_verify = 1; } #endif return ret; } #endif /* !NO_RSA || HAVE_ECC || HAVE_ED25519 || HAVE_ED448 || * HAVE_FALCON || WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */ #endif /* !NO_CERTS */ #ifdef WOLFSSL_POST_HANDSHAKE_AUTH /* Message is being processed after the enclosing handshake completed. Whatever * resumption, PSK or deferred (post-handshake) verification excused during that * handshake, a later post-handshake exchange has to stand on its own. */ #define TLS13_AFTER_HANDSHAKE(ssl) ((ssl)->options.handShakeDone) #else #define TLS13_AFTER_HANDSHAKE(ssl) 0 #endif /* Parse and handle a TLS v1.3 Finished message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of Finished. * On exit, the index of byte after the Finished message and padding. * size Length of message data. * totalSz Length of remaining data in the message buffer. * sniff Indicates whether we are sniffing packets. * returns 0 on success and otherwise failure. */ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size, word32 totalSz, int sniff) { int ret; word32 finishedSz = 0; byte* secret; byte mac[WC_MAX_DIGEST_SIZE]; WOLFSSL_START(WC_FUNC_FINISHED_DO); WOLFSSL_ENTER("DoTls13Finished"); #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) /* 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)) { #ifdef OPENSSL_COMPATIBLE_DEFAULTS if (ssl->options.isPSK && !TLS13_AFTER_HANDSHAKE(ssl)) { WOLFSSL_MSG("TLS v1.3 client used PSK but cert required. Allowing " "for OpenSSL compatibility"); } else #endif if ( #ifdef WOLFSSL_POST_HANDSHAKE_AUTH /* Exempt only the enclosing handshake; a post-handshake exchange * still requires a peer certificate and a valid * CertificateVerify. */ (!ssl->options.verifyPostHandshake || TLS13_AFTER_HANDSHAKE(ssl)) && #endif (!ssl->options.havePeerCert || !ssl->options.havePeerVerify)) { ret = NO_PEER_CERT; /* NO_PEER_VERIFY */ WOLFSSL_MSG("TLS v1.3 client did not present peer cert"); DoCertFatalAlert(ssl, ret); goto cleanup; } } #endif #if !defined(NO_CERTS) && !defined(NO_PSK) && \ defined(WOLFSSL_CERT_WITH_EXTERN_PSK) /* Verify the server sent a certificate if requested */ if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.pskNegotiated && ssl->options.failNoCert) { if ((TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) != NULL) && (!ssl->options.havePeerCert || !ssl->options.havePeerVerify)) { ret = NO_PEER_CERT; WOLFSSL_MSG("TLS v1.3 server did not present peer cert"); DoCertFatalAlert(ssl, ret); goto cleanup; } } #endif /* check against totalSz */ if (*inOutIdx + size > totalSz) { ret = BUFFER_E; goto cleanup; } #if defined(WOLFSSL_RENESAS_TSIP_TLS) ret = tsip_Tls13HandleFinished(ssl, input, inOutIdx, size, totalSz); if (ret == 0) { ssl->options.serverState = SERVER_FINISHED_COMPLETE; goto cleanup; } if (ret == WC_NO_ERR_TRACE(VERIFY_FINISHED_ERROR)) { SendAlert(ssl, alert_fatal, decrypt_error); goto cleanup; } if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { /* other errors */ goto cleanup; } ret = 0; #endif /* WOLFSSL_RENESAS_TSIP_TLS */ if (ssl->options.handShakeDone) { ret = DeriveFinishedSecret(ssl, ssl->clientSecret, ssl->keys.client_write_MAC_secret, WOLFSSL_CLIENT_END); if (ret != 0) goto cleanup; secret = ssl->keys.client_write_MAC_secret; } else if (ssl->options.side == WOLFSSL_CLIENT_END) { /* All the handshake messages have been received to calculate * client and server finished keys. */ ret = DeriveFinishedSecret(ssl, ssl->clientSecret, ssl->keys.client_write_MAC_secret, WOLFSSL_CLIENT_END); if (ret != 0) goto cleanup; ret = DeriveFinishedSecret(ssl, ssl->serverSecret, ssl->keys.server_write_MAC_secret, WOLFSSL_SERVER_END); if (ret != 0) goto cleanup; secret = ssl->keys.server_write_MAC_secret; } else { secret = ssl->keys.client_write_MAC_secret; } if (sniff == NO_SNIFF) { ret = BuildTls13HandshakeHmac(ssl, secret, mac, &finishedSz); #ifdef WOLFSSL_HAVE_TLS_UNIQUE if (finishedSz > TLS_FINISHED_SZ_MAX) { ret = BUFFER_ERROR; goto cleanup; } if (ssl->options.side == WOLFSSL_CLIENT_END) { XMEMCPY(ssl->serverFinished, mac, finishedSz); ssl->serverFinished_len = (byte)finishedSz; } else { XMEMCPY(ssl->clientFinished, mac, finishedSz); ssl->clientFinished_len = (byte)finishedSz; } #endif /* WOLFSSL_HAVE_TLS_UNIQUE */ if (ret != 0) goto cleanup; if (size != finishedSz) { ret = BUFFER_ERROR; goto cleanup; } } #ifdef WOLFSSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName(ssl, "Finished"); if (ssl->toInfoOn) AddLateName("Finished", &ssl->timeoutInfo); #endif if (sniff == NO_SNIFF) { /* Actually check verify data. */ if (size > WC_MAX_DIGEST_SIZE || ConstantCompare(input + *inOutIdx, mac, size) != 0){ WOLFSSL_MSG("Verify finished error on hashes"); SendAlert(ssl, alert_fatal, decrypt_error); WOLFSSL_ERROR_VERBOSE(VERIFY_FINISHED_ERROR); ret = VERIFY_FINISHED_ERROR; goto cleanup; } } *inOutIdx += size; #ifndef NO_WOLFSSL_SERVER if (ssl->options.side == WOLFSSL_SERVER_END && !ssl->options.handShakeDone) { #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data) { if ((ret = DeriveTls13Keys(ssl, no_key, DECRYPT_SIDE_ONLY, 1)) != 0) goto cleanup; } #endif /* Setup keys for application data messages from client. */ if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0) goto cleanup; } #endif #ifndef NO_WOLFSSL_CLIENT if (ssl->options.side == WOLFSSL_CLIENT_END) ssl->options.serverState = SERVER_FINISHED_COMPLETE; #endif #ifndef NO_WOLFSSL_SERVER if (ssl->options.side == WOLFSSL_SERVER_END) { ssl->options.clientState = CLIENT_FINISHED_COMPLETE; ssl->options.handShakeState = HANDSHAKE_DONE; ssl->options.handShakeDone = 1; } #endif #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_EARLY_DATA) if (ssl->options.dtls && ssl->earlyData > early_data_ext) { /* DTLSv1.3 has no EndOfearlydata messages. We stop processing EarlyData as soon we receive the client's finished message */ ssl->earlyData = done_early_data; } #endif /* WOLFSSL_DTLS13 && WOLFSSL_EARLY_DATA */ #if defined(WOLFSSL_QUIC) && defined(WOLFSSL_EARLY_DATA) if (WOLFSSL_IS_QUIC(ssl) && ssl->earlyData > early_data_ext) { /* QUIC has no EndOfEarlyData messages. We stop processing EarlyData as soon we receive the client's finished message */ ssl->earlyData = done_early_data; } #endif /* WOLFSSL_QUIC && WOLFSSL_EARLY_DATA */ ret = 0; cleanup: ForceZero(mac, sizeof(mac)); WOLFSSL_LEAVE("DoTls13Finished", ret); WOLFSSL_END(WC_FUNC_FINISHED_DO); return ret; } #if !defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER) /* Send the TLS v1.3 Finished message. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ static int SendTls13Finished(WOLFSSL* ssl) { byte finishedSz = ssl->specs.hash_size; byte* input; byte* output; int ret = 0; /* the resume goto can skip every build-phase assign */ int headerSz = HANDSHAKE_HEADER_SZ; int outputSz; byte* secret; #ifdef WOLFSSL_DTLS13 int dtlsRet = 0, isDtls = 0; #endif /* WOLFSSL_DTLS13 */ WOLFSSL_START(WC_FUNC_FINISHED_SEND); WOLFSSL_ENTER("SendTls13Finished"); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { headerSz = DTLS_HANDSHAKE_HEADER_SZ; /* using isDtls instead of ssl->options.dtls will abide clang static analyzer on using an uninitialized value */ isDtls = 1; } #endif /* WOLFSSL_DTLS13 */ /* Post-send key-schedule resume: the Finished record is already * queued, so skip the build phase (re-running would queue it twice). */ if (ssl->kdfDeriveStep > 0) goto tls13_send_finished_derives; ssl->options.buildingMsg = 1; outputSz = WC_MAX_DIGEST_SIZE + headerSz + MAX_MSG_EXTRA; #ifdef WOLFSSL_DTLS13 /* MAX_MSG_EXTRA reserves RECORD_HEADER_SZ, which is the size of the DTLS * 1.3 unified header without the CID, so only the CID is missing. */ if (isDtls) outputSz += DtlsGetCidTxSize(ssl); #endif /* WOLFSSL_DTLS13 */ /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) return ret; /* get output buffer */ output = GetOutputBuffer(ssl); input = output + RECORD_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (isDtls) input = output + Dtls13GetRlHeaderLength(ssl, 1); #endif /* WOLFSSL_DTLS13 */ AddTls13HandShakeHeader(input, (word32)finishedSz, 0, (word32)finishedSz, finished, ssl); #ifdef WOLFSSL_ASYNC_CRYPT /* A suspended build already wrote the verify data and hashed it; * recomputing would hash the body twice. */ if (ssl->options.buildArgs13Set) goto tls13_send_finished_encrypt; #endif #if defined(WOLFSSL_RENESAS_TSIP_TLS) if (ssl->options.side == WOLFSSL_CLIENT_END) { ret = tsip_Tls13SendFinished(ssl, output, outputSz, input, 1); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { return ret; } ret = 0; } #endif /* WOLFSSL_RENESAS_TSIP_TLS */ /* make finished hashes */ if (ssl->options.handShakeDone) { ret = DeriveFinishedSecret(ssl, ssl->clientSecret, ssl->keys.client_write_MAC_secret, WOLFSSL_CLIENT_END); if (ret != 0) return ret; secret = ssl->keys.client_write_MAC_secret; } else if (ssl->options.side == WOLFSSL_CLIENT_END) secret = ssl->keys.client_write_MAC_secret; else { /* All the handshake messages have been done to calculate client and * server finished keys. */ ret = DeriveFinishedSecret(ssl, ssl->clientSecret, ssl->keys.client_write_MAC_secret, WOLFSSL_CLIENT_END); if (ret != 0) return ret; ret = DeriveFinishedSecret(ssl, ssl->serverSecret, ssl->keys.server_write_MAC_secret, WOLFSSL_SERVER_END); if (ret != 0) return ret; secret = ssl->keys.server_write_MAC_secret; } ret = BuildTls13HandshakeHmac(ssl, secret, &input[headerSz], NULL); if (ret != 0) return ret; #ifdef WOLFSSL_HAVE_TLS_UNIQUE if (ssl->options.side == WOLFSSL_CLIENT_END) { XMEMCPY(ssl->clientFinished, &input[headerSz], finishedSz); ssl->clientFinished_len = finishedSz; } else { XMEMCPY(ssl->serverFinished, &input[headerSz], finishedSz); ssl->serverFinished_len = finishedSz; } #endif /* WOLFSSL_HAVE_TLS_UNIQUE */ #ifdef WOLFSSL_ASYNC_CRYPT tls13_send_finished_encrypt: #endif #ifdef WOLFSSL_DTLS13 if (isDtls) { dtlsRet = Dtls13HandshakeSend(ssl, output, (word16)outputSz, (word16)(Dtls13GetRlHeaderLength(ssl, 1) + headerSz + finishedSz), finished, 1); if (dtlsRet != 0 && dtlsRet != WC_NO_ERR_TRACE(WANT_WRITE)) return dtlsRet; } else #endif /* WOLFSSL_DTLS13 */ { /* This message is always encrypted. */ int sendSz = BuildTls13Message(ssl, output, outputSz, input, headerSz + finishedSz, handshake, 1, 0, TLS13_HS_ASYNC_OKAY); if (sendSz < 0) { #ifdef WOLFSSL_ASYNC_CRYPT /* Propagate a pending record encryption rather than reporting it * as a build failure: the retry resumes the record. */ if (sendSz == WC_NO_ERR_TRACE(WC_PENDING_E)) return sendSz; #endif WOLFSSL_ERROR_VERBOSE(BUILD_MSG_ERROR); return BUILD_MSG_ERROR; } #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "Finished"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "Finished", handshake, output, sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) return ret; } #endif ssl->buffers.outputBuffer.length += (word32)sendSz; ssl->options.buildingMsg = 0; } /* Build phase complete; steps below are individually resumable. */ ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_ENTERED; tls13_send_finished_derives: if (ssl->options.side == WOLFSSL_SERVER_END) { #ifdef WOLFSSL_EARLY_DATA byte storeTrafficDecKeys = ssl->earlyData == no_early_data; #endif /* Can send application data now. */ if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_ENTERED) { ret = DeriveMasterSecret(ssl); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_MASTER_SECRET; } /* Last use of preMasterSecret - zeroize as soon as possible. */ ForceZero(ssl->arrays->preMasterSecret, MAX_PREMASTER_SZ); #ifdef WOLFSSL_EARLY_DATA #ifdef WOLFSSL_DTLS13 /* DTLS13 dynamically change keys and it needs all the keys in ssl->keys to save the keying material */ if (isDtls) storeTrafficDecKeys = 1; #endif /* WOLFSSL_DTLS13 */ if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_MASTER_SECRET) { ret = DeriveTls13Keys(ssl, traffic_key, ENCRYPT_SIDE_ONLY, 1); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_ENC_TRAFFIC_KEYS; } if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_ENC_TRAFFIC_KEYS) { ret = DeriveTls13Keys(ssl, traffic_key, DECRYPT_SIDE_ONLY, storeTrafficDecKeys); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_TRAFFIC_KEYS; } #else if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_ENC_TRAFFIC_KEYS) { ret = DeriveTls13Keys(ssl, traffic_key, ENCRYPT_AND_DECRYPT_SIDE, 1); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_TRAFFIC_KEYS; } #endif if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_TRAFFIC_KEYS) { ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_ENC_KEYS_SET; } #ifdef WOLFSSL_DTLS13 if (isDtls) { w64wrapper epochTraffic0; epochTraffic0 = w64From32(0, DTLS13_EPOCH_TRAFFIC0); ssl->dtls13Epoch = epochTraffic0; ssl->dtls13PeerEpoch = epochTraffic0; if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_ENC_KEYS_SET) { ret = Dtls13SetEpochKeys(ssl, epochTraffic0, ENCRYPT_AND_DECRYPT_SIDE); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_DTLS_TRAFFIC_EPOCH; } } #endif /* WOLFSSL_DTLS13 */ } if (ssl->options.side == WOLFSSL_CLIENT_END && !ssl->options.handShakeDone) { #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data) { if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_DTLS_TRAFFIC_EPOCH) { ret = DeriveTls13Keys(ssl, no_key, ENCRYPT_SIDE_ONLY, 1); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_EARLY_ENC_KEYS; } } #endif /* Setup keys for application data messages. */ if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_EARLY_ENC_KEYS) { ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_EARLY_KEYS_SET; } #if defined(HAVE_SESSION_TICKET) if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_EARLY_KEYS_SET) { ret = DeriveResumptionSecret(ssl, ssl->session->masterSecret); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_RESUMPTION_SECRET; } #endif #ifdef WOLFSSL_DTLS13 if (isDtls) { w64wrapper epochTraffic0; epochTraffic0 = w64From32(0, DTLS13_EPOCH_TRAFFIC0); ssl->dtls13Epoch = epochTraffic0; ssl->dtls13PeerEpoch = epochTraffic0; /* Step-guarded like every other derive in this function, so a * pend resumes here and a real error clears the resume state. */ if (ssl->kdfDeriveStep <= TLS13_SEND_KDF_FIN_RESUMPTION_SECRET) { ret = Dtls13SetEpochKeys( ssl, epochTraffic0, ENCRYPT_AND_DECRYPT_SIDE); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; return ret; } ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_DTLS_EPOCH_SET; } } #endif /* WOLFSSL_DTLS13 */ } ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE; #ifndef NO_WOLFSSL_CLIENT if (ssl->options.side == WOLFSSL_CLIENT_END) { ssl->options.clientState = CLIENT_FINISHED_COMPLETE; ssl->options.handShakeState = HANDSHAKE_DONE; ssl->options.handShakeDone = 1; } #endif #ifndef NO_WOLFSSL_SERVER if (ssl->options.side == WOLFSSL_SERVER_END) { ssl->options.serverState = SERVER_FINISHED_COMPLETE; } #endif #ifdef WOLFSSL_DTLS13 if (isDtls) { WOLFSSL_LEAVE("SendTls13Finished", ret); WOLFSSL_END(WC_FUNC_FINISHED_SEND); return dtlsRet; } #endif /* WOLFSSL_DTLS13 */ if ((ret = SendBuffered(ssl)) != 0) return ret; WOLFSSL_LEAVE("SendTls13Finished", ret); WOLFSSL_END(WC_FUNC_FINISHED_SEND); return ret; } #endif /* !NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER */ /* RFC 9846 Section 4.7.3: a TLS 1.3 sender MUST NOT allow its number of key * updates to exceed 2^48-1. DTLS 1.3 bounds the epoch instead (RFC 9147 * Section 4.2.1), so this only covers TLS. * * ssl The SSL/TLS object. * returns 1 when a further KeyUpdate would exceed the limit, 0 otherwise. */ int Tls13KeyUpdateLimitReached(WOLFSSL* ssl) { if (ssl->options.dtls) return 0; return w64GTE(ssl->keys.keyUpdateCount, w64From32(TLS13_KEY_UPDATE_MAX_HI32, TLS13_KEY_UPDATE_MAX_LO32)); } /* handle generation TLS v1.3 key_update (24) */ /* Send the TLS v1.3 KeyUpdate message. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ int SendTls13KeyUpdate(WOLFSSL* ssl) { byte* input; byte* output; int ret; int headerSz = HANDSHAKE_HEADER_SZ; int outputSz; word32 i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; WOLFSSL_START(WC_FUNC_KEY_UPDATE_SEND); WOLFSSL_ENTER("SendTls13KeyUpdate"); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { /* RFC 9147 Section 4.2.1: do not send a KeyUpdate that would advance * the sending epoch beyond 2^48-1. */ if (w64GTE(ssl->dtls13Epoch, w64From32(DTLS13_EPOCH_MAX_HI32, DTLS13_EPOCH_MAX_LO32))) { WOLFSSL_MSG("DTLS 1.3 sending epoch at maximum; refusing KeyUpdate"); return BAD_STATE_E; } i = Dtls13GetRlHeaderLength(ssl, 1) + DTLS_HANDSHAKE_HEADER_SZ; } #endif /* WOLFSSL_DTLS13 */ /* RFC 9846 Section 4.7.3: a sending implementation MUST NOT allow its * number of key updates to exceed 2^48-1. Receivers MUST NOT enforce this * on the peer. */ if (Tls13KeyUpdateLimitReached(ssl)) { WOLFSSL_MSG("TLS 1.3 key update count at maximum; refusing KeyUpdate"); return BAD_STATE_E; } /* i already carries the real record and handshake header lengths. * MAX_MSG_EXTRA only budgets RECORD_HEADER_SZ. */ outputSz = (int)i + OPAQUE8_LEN + MAX_MSG_EXTRA; /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) return ret; /* get output buffer */ output = GetOutputBuffer(ssl); input = output + RECORD_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) input = output + Dtls13GetRlHeaderLength(ssl, 1); #endif /* WOLFSSL_DTLS13 */ AddTls13Headers(output, OPAQUE8_LEN, key_update, ssl); /* If: * 1. I haven't sent a KeyUpdate requesting a response and * 2. This isn't responding to peer KeyUpdate requiring a response then, * I want a response. */ ssl->keys.updateResponseReq = output[i++] = !ssl->keys.updateResponseReq && !ssl->keys.keyUpdateRespond; /* Sent response, no longer need to respond. */ ssl->keys.keyUpdateRespond = 0; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Dtls13HandshakeSend(ssl, output, (word16)outputSz, OPAQUE8_LEN + Dtls13GetRlHeaderLength(ssl, 1) + DTLS_HANDSHAKE_HEADER_SZ, key_update, 0); } else #endif /* WOLFSSL_DTLS13 */ { /* This message is always encrypted. */ int sendSz = BuildTls13Message(ssl, output, outputSz, input, headerSz + OPAQUE8_LEN, handshake, 0, 0, 0); if (sendSz < 0) return BUILD_MSG_ERROR; #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) if (ssl->hsInfoOn) AddPacketName(ssl, "KeyUpdate"); if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, "KeyUpdate", handshake, output, sendSz, WRITE_PROTO, 0, ssl->heap); if (ret != 0) return ret; } #endif ssl->buffers.outputBuffer.length += (word32)sendSz; ret = SendBuffered(ssl); if (ret != 0 && ret != WC_NO_ERR_TRACE(WANT_WRITE)) return ret; } /* In DTLS we must wait for the ack before setting up the new keys */ if (!ssl->options.dtls) { /* Future traffic uses new encryption keys. */ if ((ret = DeriveTls13Keys( ssl, update_traffic_key, ENCRYPT_SIDE_ONLY, 1)) != 0) return ret; if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0) return ret; /* Count this key update against the RFC 9846 sender limit. */ w64Increment(&ssl->keys.keyUpdateCount); } WOLFSSL_LEAVE("SendTls13KeyUpdate", ret); WOLFSSL_END(WC_FUNC_KEY_UPDATE_SEND); return ret; } /* handle processing TLS v1.3 key_update (24) */ /* Parse and handle a TLS v1.3 KeyUpdate message. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of Finished. * On exit, the index of byte after the Finished message and padding. * totalSz The length of the current handshake message. * returns 0 on success and otherwise failure. */ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 totalSz) { int ret; word32 i = *inOutIdx; WOLFSSL_START(WC_FUNC_KEY_UPDATE_DO); WOLFSSL_ENTER("DoTls13KeyUpdate"); /* check against totalSz */ if (OPAQUE8_LEN != totalSz) return BUFFER_E; switch (input[i]) { case update_not_requested: /* This message in response to any outstanding request. */ ssl->keys.keyUpdateRespond = 0; ssl->keys.updateResponseReq = 0; break; case update_requested: /* New key update requiring a response. */ ssl->keys.keyUpdateRespond = 1; break; default: WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } /* Move index to byte after message. */ *inOutIdx += totalSz; /* Future traffic uses new decryption keys. */ if ((ret = DeriveTls13Keys(ssl, update_traffic_key, DECRYPT_SIDE_ONLY, 1)) != 0) { return ret; } if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0) return ret; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { /* Increment on a local copy so ssl->dtls13PeerEpoch is left * untouched when the check fails. */ w64wrapper newEpoch = ssl->dtls13PeerEpoch; w64Increment(&newEpoch); /* RFC 9147 Section 8: the 2^48-1 cap is sender-only; receivers MUST * NOT enforce it. Guard only the wrap-to-zero (Section 4.2.1). */ if (w64IsZero(newEpoch)) return BAD_STATE_E; ssl->dtls13PeerEpoch = newEpoch; ret = Dtls13SetEpochKeys(ssl, ssl->dtls13PeerEpoch, DECRYPT_SIDE_ONLY); if (ret != 0) return ret; } #endif /* WOLFSSL_DTLS13 */ if (ssl->keys.keyUpdateRespond) { #ifdef WOLFSSL_DTLS13 /* we already sent a keyUpdate (either in response to a previous KeyUpdate or initiated by the application) and we are waiting for the ack. We can't send a new KeyUpdate right away but to honor the RFC we should send another KeyUpdate after the one in-flight is acked. We don't do that as it looks redundant, it will make the code more complex and I don't see a good use case for that. */ if (ssl->options.dtls && ssl->dtls13WaitKeyUpdateAck) { ssl->keys.keyUpdateRespond = 0; return 0; } #endif /* WOLFSSL_DTLS13 */ #if defined(HAVE_WRITE_DUP) && defined(WOLFSSL_TLS13) /* Read side cannot write; delegate the response to the write side. * The key update cap is deliberately not checked here: the two sides * are separate WOLFSSL objects with separate keys, and only the write * side ever sends a KeyUpdate, so this object's keyUpdateCount is not * the one the limit applies to. The check is applied on the write side * in wolfssl_write_dup_do_tls13_work(). */ if (ssl->dupWrite != NULL && ssl->dupSide == READ_DUP_SIDE) { if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) return BAD_MUTEX_E; ssl->dupWrite->keyUpdateRespond = 1; wc_UnLockMutex(&ssl->dupWrite->dupMutex); ssl->keys.keyUpdateRespond = 0; return 0; } #endif /* HAVE_WRITE_DUP && WOLFSSL_TLS13 */ /* RFC 9846 Section 4.7.3: a sender that would exceed the key update * limit "MUST NOT send its own KeyUpdate ... and SHOULD instead ignore * the 'update_requested' flag". Dropping the response rather than * failing keeps the connection alive on the current keys until the * Section 5.5 data limits eventually force it closed. */ if (Tls13KeyUpdateLimitReached(ssl)) { WOLFSSL_MSG("Key update limit reached; ignoring update_requested"); ssl->keys.keyUpdateRespond = 0; return 0; } #ifndef WOLFSSL_RW_THREADED return SendTls13KeyUpdate(ssl); #else ssl->options.sendKeyUpdate = 1; return 0; #endif } WOLFSSL_LEAVE("DoTls13KeyUpdate", ret); WOLFSSL_END(WC_FUNC_KEY_UPDATE_DO); return 0; } #ifdef WOLFSSL_EARLY_DATA #ifndef NO_WOLFSSL_CLIENT /* Send the TLS v1.3 EndOfEarlyData message to indicate that there will be no * more early application data. * The encryption key now changes to the pre-calculated handshake key. * * ssl The SSL/TLS object. * returns 0 on success and otherwise failure. */ static int SendTls13EndOfEarlyData(WOLFSSL* ssl) { byte* output; int ret; int sendSz; word32 length; word32 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; WOLFSSL_START(WC_FUNC_END_OF_EARLY_DATA_SEND); WOLFSSL_ENTER("SendTls13EndOfEarlyData"); length = 0; sendSz = (int)(idx + length + MAX_MSG_EXTRA); ssl->options.buildingMsg = 1; /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, sendSz)) != 0) return ret; /* Get position in output buffer to write new message to. */ output = GetOutputBuffer(ssl); /* Put the record and handshake headers on. */ AddTls13Headers(output, length, end_of_early_data, ssl); /* This message is always encrypted. */ sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ, idx - RECORD_HEADER_SZ, handshake, 1, 0, 0); if (sendSz < 0) return sendSz; ssl->buffers.outputBuffer.length += sendSz; if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0) return ret; ssl->options.buildingMsg = 0; if (!ssl->options.groupMessages) ret = SendBuffered(ssl); ssl->earlyData = done_early_data; WOLFSSL_LEAVE("SendTls13EndOfEarlyData", ret); WOLFSSL_END(WC_FUNC_END_OF_EARLY_DATA_SEND); return ret; } #endif /* !NO_WOLFSSL_CLIENT */ #ifndef NO_WOLFSSL_SERVER /* handle processing of TLS 1.3 end_of_early_data (5) */ /* Parse the TLS v1.3 EndOfEarlyData message that indicates that there will be * no more early application data. * The decryption key now changes to the pre-calculated handshake key. * * ssl The SSL/TLS object. * returns 0 on success and otherwise failure. */ static int DoTls13EndOfEarlyData(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size) { int ret; word32 begin = *inOutIdx; (void)input; WOLFSSL_START(WC_FUNC_END_OF_EARLY_DATA_DO); WOLFSSL_ENTER("DoTls13EndOfEarlyData"); if ((*inOutIdx - begin) != size) return BUFFER_ERROR; if (ssl->earlyData == no_early_data) { WOLFSSL_MSG("EndOfEarlyData received unexpectedly"); SendAlert(ssl, alert_fatal, unexpected_message); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } ssl->earlyData = done_early_data; ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY); WOLFSSL_LEAVE("DoTls13EndOfEarlyData", ret); WOLFSSL_END(WC_FUNC_END_OF_EARLY_DATA_DO); return ret; } #endif /* !NO_WOLFSSL_SERVER */ #endif /* WOLFSSL_EARLY_DATA */ #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) int SessionTicketNoncePopulate(WOLFSSL_SESSION *session, const byte *nonce, byte len) { if (session->ticketNonce.data != session->ticketNonce.dataStatic) { XFREE(session->ticketNonce.data, session->heap, DYNAMIC_TYPE_SESSION_TICK); session->ticketNonce.data = session->ticketNonce.dataStatic; session->ticketNonce.len = 0; } if (len > MAX_TICKET_NONCE_STATIC_SZ) { WOLFSSL_MSG("Using dynamic nonce buffer"); session->ticketNonce.data = (byte*)XMALLOC(len, session->heap, DYNAMIC_TYPE_SESSION_TICK); if (session->ticketNonce.data == NULL) return MEMORY_ERROR; } XMEMCPY(session->ticketNonce.data, nonce, len); session->ticketNonce.len = len; return 0; } #endif #ifndef NO_WOLFSSL_CLIENT /* Handle a New Session Ticket handshake message. * Message contains the information required to perform resumption. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the message buffer of NewSessionTicket. * On exit, the index of byte after the NewSessionTicket message. * Record padding is skipped by the caller. * size The length of the current handshake message. * returns 0 on success, otherwise failure. */ static int DoTls13NewSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size) { #ifdef HAVE_SESSION_TICKET int ret; word32 begin = *inOutIdx; word32 lifetime; word32 ageAdd; word16 length; #ifdef WOLFSSL_32BIT_MILLI_TIME word32 now; #else sword64 now; #endif const byte* nonce; byte nonceLength; WOLFSSL_START(WC_FUNC_NEW_SESSION_TICKET_DO); WOLFSSL_ENTER("DoTls13NewSessionTicket"); #ifdef HAVE_ECH /* ignore session ticket when ECH is rejected */ if (ssl->echConfigs != NULL && !ssl->options.disableECH && !ssl->options.echAccepted) { *inOutIdx += size; return 0; } #endif /* Lifetime hint. */ if ((*inOutIdx - begin) + SESSION_HINT_SZ > size) return BUFFER_ERROR; ato32(input + *inOutIdx, &lifetime); *inOutIdx += SESSION_HINT_SZ; if (lifetime > MAX_LIFETIME) { WOLFSSL_ERROR_VERBOSE(SERVER_HINT_ERROR); return SERVER_HINT_ERROR; } /* Age add. */ if ((*inOutIdx - begin) + SESSION_ADD_SZ > size) return BUFFER_ERROR; ato32(input + *inOutIdx, &ageAdd); *inOutIdx += SESSION_ADD_SZ; /* Ticket nonce. */ if ((*inOutIdx - begin) + 1 > size) return BUFFER_ERROR; nonceLength = input[*inOutIdx]; #if !defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3)) if (nonceLength > MAX_TICKET_NONCE_STATIC_SZ) { WOLFSSL_MSG("Nonce length not supported"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } #endif /* WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3) */ *inOutIdx += 1; if ((*inOutIdx - begin) + nonceLength > size) return BUFFER_ERROR; nonce = input + *inOutIdx; *inOutIdx += nonceLength; /* Ticket length. */ if ((*inOutIdx - begin) + LENGTH_SZ > size) return BUFFER_ERROR; ato16(input + *inOutIdx, &length); *inOutIdx += LENGTH_SZ; if ((*inOutIdx - begin) + length > size) return BUFFER_ERROR; /* note: we reject zero length ticket here, and not in SetTicket(), * because zero length is valid for TLS 1.2 */ if (length == 0) return BUFFER_ERROR; if ((ret = SetTicket(ssl, input + *inOutIdx, length)) != 0) return ret; *inOutIdx += length; now = TimeNowInMilliseconds(); if (now == 0) return GETTIME_ERROR; /* Copy in ticket data (server identity). */ ssl->timeout = lifetime; ssl->session->timeout = lifetime; ssl->session->cipherSuite0 = ssl->options.cipherSuite0; ssl->session->cipherSuite = ssl->options.cipherSuite; ssl->session->ticketSeen = now; ssl->session->ticketAdd = ageAdd; #ifdef WOLFSSL_EARLY_DATA ssl->session->maxEarlyDataSz = ssl->options.maxEarlyDataSz; #endif #if defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) ret = SessionTicketNoncePopulate(ssl->session, nonce, nonceLength); if (ret != 0) return ret; #else ssl->session->ticketNonce.len = nonceLength; if (nonceLength > MAX_TICKET_NONCE_STATIC_SZ) { ret = BUFFER_ERROR; return ret; } if (nonceLength > 0) XMEMCPY(ssl->session->ticketNonce.data, nonce, nonceLength); #endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */ ssl->session->namedGroup = ssl->namedGroup; if ((*inOutIdx - begin) + EXTS_SZ > size) return BUFFER_ERROR; ato16(input + *inOutIdx, &length); *inOutIdx += EXTS_SZ; if ((*inOutIdx - begin) + length != size) return BUFFER_ERROR; /* RFC 9846 Section 4.7.1: the extensions are Section 4.3 Extension TLVs. * Malformed framing is a syntax error even when no extension in the list * is one we act on. */ ret = TLSX_Parse(ssl, input + *inOutIdx, length, session_ticket, NULL); if (ret != 0) return ret; *inOutIdx += length; SetupSession(ssl); #ifndef NO_SESSION_CACHE AddSession(ssl); #endif ssl->expect_session_ticket = 0; #else (void)ssl; (void)input; WOLFSSL_ENTER("DoTls13NewSessionTicket"); *inOutIdx += size; #endif /* HAVE_SESSION_TICKET */ WOLFSSL_LEAVE("DoTls13NewSessionTicket", 0); WOLFSSL_END(WC_FUNC_NEW_SESSION_TICKET_DO); return 0; } #endif /* NO_WOLFSSL_CLIENT */ #ifndef NO_WOLFSSL_SERVER #ifdef HAVE_SESSION_TICKET #ifdef WOLFSSL_TLS13_TICKET_BEFORE_FINISHED /* Offset of the MAC size in the finished message. */ #define FINISHED_MSG_SIZE_OFFSET 3 /* Calculate the resumption secret which includes the unseen client finished * message. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ static int ExpectedResumptionSecret(WOLFSSL* ssl) { int ret; int saveRet = 0; word32 finishedSz = 0; byte mac[WC_MAX_DIGEST_SIZE]; Digest digest; byte header[] = { 0x14, 0x00, 0x00, 0x00 }; XMEMSET(&digest, 0, sizeof(Digest)); /* Copy the running hash so we can restore it after. */ switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: ret = wc_Sha256Copy(&ssl->hsHashes->hashSha256, &digest.sha256); if (ret != 0) return ret; break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: ret = wc_Sha384Copy(&ssl->hsHashes->hashSha384, &digest.sha384); if (ret != 0) return ret; break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: ret = wc_Sha512Copy(&ssl->hsHashes->hashSha512, &digest.sha512); if (ret != 0) return ret; break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: ret = wc_Sm3Copy(&ssl->hsHashes->hashSm3, &digest.sm3); if (ret != 0) return ret; break; #endif } #ifdef WOLFSSL_EARLY_DATA /* The client only sends EndOfEarlyData when early data was accepted, and * never with DTLS or QUIC. It comes before, and is covered by, the * client's Finished message. */ if (ssl->earlyData == process_early_data && !ssl->options.dtls && !WOLFSSL_IS_QUIC(ssl)) { static const byte endOfEarlyData[] = { 0x05, 0x00, 0x00, 0x00 }; ret = HashRaw(ssl, endOfEarlyData, sizeof(endOfEarlyData)); if (ret != 0) goto restore; } #endif /* Generate the Client's Finished message and hash it. */ ret = BuildTls13HandshakeHmac(ssl, ssl->keys.client_write_MAC_secret, mac, &finishedSz); if (ret != 0) goto restore; header[FINISHED_MSG_SIZE_OFFSET] = finishedSz; if ((ret = HashRaw(ssl, header, sizeof(header))) != 0) goto restore; if ((ret = HashRaw(ssl, mac, finishedSz)) != 0) goto restore; if ((ret = DeriveResumptionSecret(ssl, ssl->session->masterSecret)) != 0) goto restore; /* Restore the hash inline with currently seen messages. */ restore: /* The restore result must not mask the error that got here, or a * WC_PENDING_E would be reported as success with the derive skipped. */ saveRet = ret; switch (ssl->specs.mac_algorithm) { #ifndef NO_SHA256 case sha256_mac: wc_Sha256Free(&ssl->hsHashes->hashSha256); ret = wc_Sha256Copy(&digest.sha256, &ssl->hsHashes->hashSha256); wc_Sha256Free(&digest.sha256); break; #endif #ifdef WOLFSSL_SHA384 case sha384_mac: wc_Sha384Free(&ssl->hsHashes->hashSha384); ret = wc_Sha384Copy(&digest.sha384, &ssl->hsHashes->hashSha384); wc_Sha384Free(&digest.sha384); break; #endif #ifdef WOLFSSL_TLS13_SHA512 case sha512_mac: wc_Sha512Free(&ssl->hsHashes->hashSha512); ret = wc_Sha512Copy(&digest.sha512, &ssl->hsHashes->hashSha512); wc_Sha512Free(&digest.sha512); break; #endif #ifdef WOLFSSL_SM3 case sm3_mac: wc_Sm3Free(&ssl->hsHashes->hashSm3); ret = wc_Sm3Copy(&digest.sm3, &ssl->hsHashes->hashSm3); wc_Sm3Free(&digest.sm3); break; #endif } if (saveRet != 0) ret = saveRet; ForceZero(mac, sizeof(mac)); return ret; } #endif /* Check the client advertised a PSK key exchange mode a resumption ticket can * be used with. * * RFC 9846 Section 4.3.9: psk_key_exchange_modes restricts both the PSKs * offered in the ClientHello and those the server might supply through * NewSessionTicket, and servers should not send tickets that are incompatible * with the advertised modes. RFC 9846 Section 4.7.1 makes sending a ticket * conditional on the client's hello carrying a suitable extension. * * ssl The SSL/TLS object. * returns 0 when a ticket may be sent, MISSING_HANDSHAKE_DATA when the * extension was not received and PSK_KEY_ERROR when none of the * advertised modes is usable. */ static int CheckTls13TicketPskModes(WOLFSSL* ssl) { #ifdef WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES if (!ssl->options.pskKeModesRecvd) { WOLFSSL_MSG("No psk_key_exchange_modes in ClientHello"); return MISSING_HANDSHAKE_DATA; } if ((ssl->options.pskKeModes & (1 << PSK_KE)) != 0 #ifdef HAVE_SUPPORTED_CURVES && !ssl->options.onlyPskDheKe #endif ) { return 0; } /* The configured policy, not noPskDheKe - a certificate handshake clears * that one before the ticket is sent, which would make this always true. */ if ((ssl->options.pskKeModes & (1 << PSK_DHE_KE)) != 0 && !ssl->options.noPskDheKePolicy) { return 0; } WOLFSSL_MSG("No usable psk_key_exchange_modes advertised by client"); return PSK_KEY_ERROR; #else (void)ssl; return 0; #endif } /* Send New Session Ticket handshake message. * Message contains the information required to perform resumption. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ static int SendTls13NewSessionTicket(WOLFSSL* ssl) { byte* output; int ret; word32 length; int sendSz; word16 extSz; int genAltSessionID; word32 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; WOLFSSL_START(WC_FUNC_NEW_SESSION_TICKET_SEND); WOLFSSL_ENTER("SendTls13NewSessionTicket"); if (DefTicketHintTooLarge(ssl)) { WOLFSSL_MSG("Ticket hint exceeds half the ticket key lifetime; " "skipping ticket"); return 0; } if (CheckTls13TicketPskModes(ssl) != 0) { WOLFSSL_MSG("Client advertised no usable PSK key exchange mode; " "skipping ticket"); return 0; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) idx = Dtls13GetRlHeaderLength(ssl, 1) + DTLS_HANDSHAKE_HEADER_SZ; #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_TLS13_TICKET_BEFORE_FINISHED if (!ssl->msgsReceived.got_finished) { if ((ret = ExpectedResumptionSecret(ssl)) != 0) return ret; } #endif /* Start ticket nonce at 0 and go up to 255. */ if (ssl->session->ticketNonce.len == 0) { ssl->session->ticketNonce.len = DEF_TICKET_NONCE_SZ; ssl->session->ticketNonce.data[0] = 0; } else #ifdef WOLFSSL_ASYNC_CRYPT if (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)) #endif { if (ssl->session->ticketNonce.data[0] == 255) { /* RFC8446 Section 4.6.1: Each ticket must have a unique nonce * value. As the nonce is only a single byte, we have to prevent * the overflow and abort. */ return SESSION_TICKET_NONCE_OVERFLOW; } else ssl->session->ticketNonce.data[0]++; } /* Generate a new ID for the new ticket so that we don't overwrite any old * ones. Only an ID ticket is cached without WOLFSSL_TICKET_HAVE_ID. */ genAltSessionID = (ssl->options.mask & WOLFSSL_OP_NO_TICKET) != 0; #if defined(WOLFSSL_TICKET_HAVE_ID) && defined(WOLFSSL_EARLY_DATA) && \ !defined(NO_SESSION_CACHE) /* A 0-RTT capable ticket needs a cache entry of its own to back the * single-use bound of RFC 8446 Sect. 8. Sessions that cannot send early * data keep one ID, so state bound to the cache entry (ex_data) survives * a resumption. */ if (ssl->options.maxEarlyDataSz > 0) genAltSessionID = 1; #endif #ifdef WOLFSSL_ASYNC_CRYPT /* CreateTicket() already copied the ID into the ticket being encrypted, * so keep it across an async retry. */ if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) genAltSessionID = 0; #endif if (genAltSessionID) { ret = SessionNewAltId(ssl); if (ret != 0) return ret; } if (!ssl->options.noTicketTls13) { if ((ret = SetupTicket(ssl)) != 0) return ret; /* No need to create the ticket if we only send the ID */ if ((ssl->options.mask & WOLFSSL_OP_NO_TICKET) == 0) { if ((ret = CreateTicket(ssl)) != 0) return ret; } } #ifdef WOLFSSL_EARLY_DATA ssl->session->maxEarlyDataSz = ssl->options.maxEarlyDataSz; if (ssl->session->maxEarlyDataSz > 0) TLSX_EarlyData_Use(ssl, ssl->session->maxEarlyDataSz, 1); extSz = 0; ret = TLSX_GetResponseSize(ssl, session_ticket, &extSz); if (ret != 0) return ret; #else extSz = EXTS_SZ; #endif /* Lifetime | Age Add | Ticket session ID | Extensions */ length = SESSION_HINT_SZ + SESSION_ADD_SZ + LENGTH_SZ; if ((ssl->options.mask & WOLFSSL_OP_NO_TICKET) != 0) length += ID_LEN + extSz; else length += ssl->session->ticketLen + extSz; /* Nonce */ length += TICKET_NONCE_LEN_SZ + DEF_TICKET_NONCE_SZ; sendSz = (int)(idx + length + MAX_MSG_EXTRA); /* Check buffers are big enough and grow if needed. */ if ((ret = CheckAvailableSize(ssl, sendSz)) != 0) return ret; /* Get position in output buffer to write new message to. */ output = GetOutputBuffer(ssl); /* Put the record and handshake headers on. */ AddTls13Headers(output, length, session_ticket, ssl); /* Lifetime hint */ c32toa(ssl->ctx->ticketHint, output + idx); idx += SESSION_HINT_SZ; /* Age add - obfuscator */ c32toa(ssl->session->ticketAdd, output + idx); idx += SESSION_ADD_SZ; output[idx++] = ssl->session->ticketNonce.len; output[idx++] = ssl->session->ticketNonce.data[0]; /* length */ if ((ssl->options.mask & WOLFSSL_OP_NO_TICKET) != 0) { c16toa(ID_LEN, output + idx); } else { c16toa(ssl->session->ticketLen, output + idx); } idx += LENGTH_SZ; /* ticket */ if ((ssl->options.mask & WOLFSSL_OP_NO_TICKET) != 0) { if (ssl->session->haveAltSessionID) XMEMCPY(output + idx, ssl->session->altSessionID, ID_LEN); else return BAD_FUNC_ARG; /* Should not happen */ idx += ID_LEN; } else { XMEMCPY(output + idx, ssl->session->ticket, ssl->session->ticketLen); idx += ssl->session->ticketLen; } #ifdef WOLFSSL_EARLY_DATA extSz = 0; ret = TLSX_WriteResponse(ssl, output + idx, session_ticket, &extSz); if (ret != 0) return ret; idx += extSz; #else /* No extension support - empty extensions. */ c16toa(0, output + idx); idx += EXTS_SZ; #endif if (idx > WOLFSSL_MAX_16BIT || sendSz > (int)WOLFSSL_MAX_16BIT) { return BAD_LENGTH_E; } ssl->options.haveSessionId = 1; SetupSession(ssl); /* Only add to cache when support built in and when the ticket contains * an ID. Otherwise we have no way to actually retrieve the ticket from the * cache. */ #if !defined(NO_SESSION_CACHE) && defined(WOLFSSL_TICKET_HAVE_ID) AddSession(ssl); #endif #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) return Dtls13HandshakeSend(ssl, output, (word16)sendSz, (word16)idx, session_ticket, 0); #endif /* WOLFSSL_DTLS13 */ /* This message is always encrypted. */ sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ, (word16)idx - RECORD_HEADER_SZ, handshake, 0, 0, 0); if (sendSz < 0) return sendSz; ssl->buffers.outputBuffer.length += sendSz; /* Always send as this is either directly after server's Finished or only * message after client's Finished. */ ret = SendBuffered(ssl); WOLFSSL_LEAVE("SendTls13NewSessionTicket", 0); WOLFSSL_END(WC_FUNC_NEW_SESSION_TICKET_SEND); return ret; } #endif /* HAVE_SESSION_TICKET */ #endif /* NO_WOLFSSL_SERVER */ /* Make sure no duplicates, no fast forward, or other problems * * ssl The SSL/TLS object. * type Type of handshake message received. * returns 0 on success, otherwise failure. */ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) { /* verify not a duplicate, mark received, check state */ switch (type) { #ifndef NO_WOLFSSL_SERVER case client_hello: #ifndef NO_WOLFSSL_CLIENT /* Only valid when received on SERVER side. */ if (ssl->options.side == WOLFSSL_CLIENT_END) { WOLFSSL_MSG("ClientHello received by client"); WOLFSSL_ERROR_VERBOSE(SIDE_ERROR); return SIDE_ERROR; } #endif /* A replay after a pend arrives with got_client_hello cleared * (see exit_dch); a genuine duplicate arrives with it set. */ if (ssl->options.clientState >= CLIENT_HELLO_COMPLETE #ifdef WOLFSSL_ASYNC_CRYPT && ssl->msgsReceived.got_client_hello != 0 #endif ) { WOLFSSL_MSG("ClientHello received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Check previously seen. */ /* Initial and after HelloRetryRequest - no more than 2. */ if (ssl->msgsReceived.got_client_hello == 2) { WOLFSSL_MSG("Too many ClientHello received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } /* Second only after HelloRetryRequest seen. */ if (ssl->msgsReceived.got_client_hello == 1 && ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE) { WOLFSSL_MSG("Duplicate ClientHello received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_client_hello++; break; #endif #ifndef NO_WOLFSSL_CLIENT case server_hello: #ifndef NO_WOLFSSL_SERVER /* Only valid when received on CLIENT side. */ if (ssl->options.side == WOLFSSL_SERVER_END) { WOLFSSL_MSG("ServerHello received by server"); WOLFSSL_ERROR_VERBOSE(SIDE_ERROR); return SIDE_ERROR; } #endif /* Check state. */ if (ssl->options.serverState >= SERVER_HELLO_COMPLETE) { WOLFSSL_MSG("ServerHello received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Check previously seen. */ /* Only once after ClientHello. * HelloRetryRequest has ServerHello type but count fixed up later * - see DoTls13ServerHello(). */ if (ssl->msgsReceived.got_server_hello) { WOLFSSL_MSG("Duplicate ServerHello received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_server_hello = 1; break; #endif #ifndef NO_WOLFSSL_CLIENT case session_ticket: #ifndef NO_WOLFSSL_SERVER /* Only valid when received on CLIENT side. */ if (ssl->options.side == WOLFSSL_SERVER_END) { WOLFSSL_MSG("NewSessionTicket received by server"); WOLFSSL_ERROR_VERBOSE(SIDE_ERROR); return SIDE_ERROR; } #endif /* Check state. */ #ifdef WOLFSSL_TLS13_TICKET_BEFORE_FINISHED /* Only allowed after server's Finished message. */ if (ssl->options.serverState < SERVER_FINISHED_COMPLETE) { WOLFSSL_MSG("NewSessionTicket received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #else /* Only allowed after client's Finished message. */ if (ssl->options.clientState < CLIENT_FINISHED_COMPLETE) { WOLFSSL_MSG("NewSessionTicket received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #endif /* Many SessionTickets can be sent. */ ssl->msgsReceived.got_session_ticket = 1; break; #endif #ifndef NO_WOLFSSL_SERVER #ifdef WOLFSSL_EARLY_DATA case end_of_early_data: #ifndef NO_WOLFSSL_CLIENT /* Only valid when received on SERVER side. */ if (ssl->options.side == WOLFSSL_CLIENT_END) { WOLFSSL_MSG("EndOfEarlyData received by client"); WOLFSSL_ERROR_VERBOSE(SIDE_ERROR); return SIDE_ERROR; } #endif /* Check state. */ /* Only after server's Finished and before client's Finished. */ if (ssl->options.serverState < SERVER_FINISHED_COMPLETE) { WOLFSSL_MSG("EndOfEarlyData received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (ssl->options.clientState >= CLIENT_FINISHED_COMPLETE) { WOLFSSL_MSG("EndOfEarlyData received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Check previously seen. */ if (ssl->msgsReceived.got_end_of_early_data) { WOLFSSL_MSG("Too many EndOfEarlyData received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_end_of_early_data = 1; break; #endif #endif #ifndef NO_WOLFSSL_CLIENT case encrypted_extensions: #ifndef NO_WOLFSSL_SERVER /* Only valid when received on CLIENT side. */ if (ssl->options.side == WOLFSSL_SERVER_END) { WOLFSSL_MSG("EncryptedExtensions received by server"); WOLFSSL_ERROR_VERBOSE(SIDE_ERROR); return SIDE_ERROR; } #endif /* Check state. */ /* Must be received directly after ServerHello. * DoTls13EncryptedExtensions() changes state to: * SERVER_ENCRYPTED_EXTENSIONS_COMPLETE. */ if (ssl->options.serverState != SERVER_HELLO_COMPLETE) { WOLFSSL_MSG("EncryptedExtensions received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Check previously seen. */ if (ssl->msgsReceived.got_encrypted_extensions) { WOLFSSL_MSG("Duplicate EncryptedExtensions received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_encrypted_extensions = 1; break; #endif case certificate: /* Valid on both sides. */ #ifndef NO_WOLFSSL_CLIENT /* Check state. */ /* On client, seen after EncryptedExtension and CertificateRequest * (if sent) and before CertificateVerify and Finished. * DoTls13Certificate() sets serverState to SERVER_CERT_COMPLETE. */ if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.serverState != SERVER_ENCRYPTED_EXTENSIONS_COMPLETE) { WOLFSSL_MSG("Certificate received out of order - Client"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* Server's authenticating with PSK must not send this. */ if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.serverState == SERVER_CERT_COMPLETE && ssl->options.pskNegotiated #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK && !ssl->options.certWithExternPsk #endif ) { WOLFSSL_MSG("Certificate received while using PSK"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } #endif #endif #ifndef NO_WOLFSSL_SERVER /* Check state. */ /* On Server, valid after ClientHello received and ServerFinished * sent. */ if (ssl->options.side == WOLFSSL_SERVER_END && ssl->options.clientState != CLIENT_HELLO_COMPLETE && ssl->options.serverState < SERVER_FINISHED_COMPLETE) { WOLFSSL_MSG("Certificate received out of order - Server"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* RFC 8446 4.4.2: the client only sends this in response to a * CertificateRequest, which a server authenticating with a PSK * does not send in the main handshake (but may post-handshake). */ if (ssl->options.side == WOLFSSL_SERVER_END && ssl->options.pskNegotiated && !TLS13_AFTER_HANDSHAKE(ssl) #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK && !ssl->options.certWithExternPsk #endif ) { WOLFSSL_MSG("Certificate received while using PSK - Server"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } #endif #endif /* Check previously seen. */ if (ssl->msgsReceived.got_certificate) { WOLFSSL_MSG("Duplicate Certificate received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_certificate = 1; break; #ifndef NO_WOLFSSL_CLIENT case certificate_request: #ifndef NO_WOLFSSL_SERVER /* Only valid when received on CLIENT side. */ if (ssl->options.side == WOLFSSL_SERVER_END) { WOLFSSL_MSG("CertificateRequest received by server"); WOLFSSL_ERROR_VERBOSE(SIDE_ERROR); return SIDE_ERROR; } #endif /* Check state. */ #ifndef WOLFSSL_POST_HANDSHAKE_AUTH /* Only valid when sent after EncryptedExtensions and before * Certificate. */ if (ssl->options.serverState != SERVER_ENCRYPTED_EXTENSIONS_COMPLETE) { WOLFSSL_MSG("CertificateRequest received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #else /* Valid when sent after EncryptedExtensions and before Certificate * and after both client and server have sent Finished (Post * Handshake Authentication). */ if (ssl->options.serverState != SERVER_ENCRYPTED_EXTENSIONS_COMPLETE && (ssl->options.serverState < SERVER_FINISHED_COMPLETE || ssl->options.clientState != CLIENT_FINISHED_COMPLETE)) { WOLFSSL_MSG("CertificateRequest received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* RFC 8446 4.6.2: A client that receives a post-handshake * CertificateRequest message without having sent the * "post_handshake_auth" extension MUST send an * "unexpected_message" fatal alert. wolfSSL_allow_post_handshake_auth() * must be called before wolfSSL_connect() so postHandshakeAuth * reflects whether the extension was offered. */ if (ssl->options.serverState >= SERVER_FINISHED_COMPLETE && ssl->options.clientState == CLIENT_FINISHED_COMPLETE && !ssl->options.postHandshakeAuth) { WOLFSSL_MSG("Post-handshake CertificateRequest received " "without having sent post_handshake_auth " "extension"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* RFC 8446 4.3.2: a server authenticating with a PSK must not send * this in the main handshake, but may send it post-handshake. */ if (ssl->options.pskNegotiated && !TLS13_AFTER_HANDSHAKE(ssl) #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK && !ssl->options.certWithExternPsk #endif ) { WOLFSSL_MSG("CertificateRequest received while using PSK"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } #endif /* Check previously seen. */ #ifndef WOLFSSL_POST_HANDSHAKE_AUTH /* Only once during handshake. */ if (ssl->msgsReceived.got_certificate_request) { WOLFSSL_MSG("Duplicate CertificateRequest received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } #else /* Only once during handshake. */ if (ssl->msgsReceived.got_certificate_request && ssl->options.clientState != CLIENT_FINISHED_COMPLETE) { WOLFSSL_MSG("Duplicate CertificateRequest received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } #endif ssl->msgsReceived.got_certificate_request = 1; break; #endif case certificate_verify: /* Valid on both sides. */ #ifndef NO_WOLFSSL_CLIENT /* Check state on client. * Valid only directly after a Certificate message. */ if (ssl->options.side == WOLFSSL_CLIENT_END) { if (ssl->options.serverState != SERVER_CERT_COMPLETE) { WOLFSSL_MSG("No Cert before CertVerify"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* Server's authenticating with PSK must not send this. */ if (ssl->options.pskNegotiated #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK && !ssl->options.certWithExternPsk #endif ) { WOLFSSL_MSG("CertificateVerify received while using PSK"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } #endif } #endif #ifndef NO_WOLFSSL_SERVER /* Check state on server. */ if (ssl->options.side == WOLFSSL_SERVER_END) { /* Server must have sent Finished message. */ if (ssl->options.serverState < SERVER_FINISHED_COMPLETE) { WOLFSSL_MSG("CertificateVerify received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Valid only directly after a Certificate message. */ if (ssl->options.clientState < CLIENT_HELLO_COMPLETE) { WOLFSSL_MSG("CertificateVerify before ClientHello done"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (!ssl->msgsReceived.got_certificate) { WOLFSSL_MSG("No Cert before CertificateVerify"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } } #endif /* Check previously seen. */ if (ssl->msgsReceived.got_certificate_verify) { WOLFSSL_MSG("Duplicate CertificateVerify received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_certificate_verify = 1; break; case finished: /* Valid on both sides. */ #ifndef NO_WOLFSSL_CLIENT /* Check state on client. */ if (ssl->options.side == WOLFSSL_CLIENT_END) { /* After sending ClientHello */ if (ssl->options.clientState < CLIENT_HELLO_COMPLETE) { WOLFSSL_MSG("Finished received out of order - clientState"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Must have seen certificate and verify from server except when * using PSK. */ #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) if (ssl->options.pskNegotiated) { #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK if (ssl->options.certWithExternPsk) { if (ssl->options.serverState != SERVER_CERT_VERIFY_COMPLETE) { WOLFSSL_MSG("Finished received out of order - " "cert_with_extern_psk"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } } else #endif { if (ssl->options.serverState != SERVER_ENCRYPTED_EXTENSIONS_COMPLETE) { WOLFSSL_MSG("Finished received out of order - PSK"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } } } else #endif if (ssl->options.serverState != SERVER_CERT_VERIFY_COMPLETE) { WOLFSSL_MSG("Finished received out of order - serverState"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } } #endif #ifndef NO_WOLFSSL_SERVER /* Check state on server. */ if (ssl->options.side == WOLFSSL_SERVER_END) { if (ssl->options.serverState < SERVER_FINISHED_COMPLETE) { WOLFSSL_MSG("Finished received out of order - serverState"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (ssl->options.clientState < CLIENT_HELLO_COMPLETE) { WOLFSSL_MSG("Finished received out of order - clientState"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData == process_early_data && /* early data may be lost when using DTLS */ !ssl->options.dtls /* QUIC does not use EndOfEarlyData records */ && !WOLFSSL_IS_QUIC(ssl)) { WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } #endif } #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) if (!ssl->options.pskNegotiated || (ssl->options.side == WOLFSSL_SERVER_END && TLS13_AFTER_HANDSHAKE(ssl)) #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK || ssl->options.certWithExternPsk #endif ) #endif { /* Must have received a Certificate message from client if * verifying the peer. Empty certificate message indicates * no certificate available. */ if (ssl->options.verifyPeer && #ifdef WOLFSSL_POST_HANDSHAKE_AUTH /* The post-handshake-auth exemption is only valid during * the enclosing handshake. Once the server has requested a * certificate post-handshake, one is required again. * Whether an empty Certificate is then accepted follows the * verify mode (FAIL_IF_NO_PEER_CERT), exactly as for * first-handshake client authentication. */ (!ssl->options.verifyPostHandshake || TLS13_AFTER_HANDSHAKE(ssl)) && #endif !ssl->msgsReceived.got_certificate) { WOLFSSL_MSG("Finished received out of order - " "missing Certificate message"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Mutual authentication on server requires a certificate from * peer. Verify peer set on client side requires a certificate * from peer as not doing PSK. */ if ((ssl->options.mutualAuth || (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.verifyPeer)) && !ssl->options.havePeerCert) { WOLFSSL_MSG("Finished received out of order - " "no valid certificate"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } } /* Must have received a valid CertificateVerify if got a peer * certificate. A certificate without proof of possession is never * acceptable, regardless of how the handshake was authenticated. */ if (ssl->options.havePeerCert && !ssl->options.havePeerVerify) { WOLFSSL_MSG("Finished received out of order - " "Certificate message but no CertificateVerify"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Check previously seen. */ if (ssl->msgsReceived.got_finished) { WOLFSSL_MSG("Duplicate Finished received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_finished = 1; break; case key_update: /* Valid on both sides. */ #ifdef WOLFSSL_QUIC /* RFC 9001 Section 6: QUIC performs key updates at the QUIC * packet-protection layer, so a TLS KeyUpdate message must be * rejected as a fatal unexpected_message connection error. The * caller sends that alert for any sanity failure. */ if (WOLFSSL_IS_QUIC(ssl)) { WOLFSSL_MSG("KeyUpdate received over QUIC"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } #endif /* Check state. * Client and server must have received finished message from other * side. */ if (!ssl->msgsReceived.got_finished) { WOLFSSL_MSG("No KeyUpdate before Finished"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* Multiple KeyUpdates can be sent. */ break; #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_NO_TLS12) case hello_verify_request: if (!ssl->options.dtls) { WOLFSSL_MSG("HelloVerifyRequest when not in DTLS"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (ssl->msgsReceived.got_hello_verify_request) { WOLFSSL_MSG("Duplicate HelloVerifyRequest received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } ssl->msgsReceived.got_hello_verify_request = 1; if (ssl->msgsReceived.got_hello_retry_request) { WOLFSSL_MSG( "Both HelloVerifyRequest and HelloRetryRequest received"); WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E); return DUPLICATE_MSG_E; } if (ssl->options.serverState >= SERVER_HELLO_RETRY_REQUEST_COMPLETE || ssl->options.connectState != CLIENT_HELLO_SENT) { WOLFSSL_MSG("HelloVerifyRequest received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (ssl->options.side == WOLFSSL_SERVER_END) { WOLFSSL_MSG("HelloVerifyRequest received on the server"); WOLFSSL_ERROR_VERBOSE(SIDE_ERROR); return SIDE_ERROR; } if (!ssl->options.downgrade || ssl->options.minDowngrade < DTLSv1_2_MINOR) { WOLFSSL_MSG( "HelloVerifyRequest received but not DTLSv1.2 allowed"); WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } break; #endif /* WOLFSSL_DTLS13 && !WOLFSSL_NO_TLS12*/ #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) case request_connection_id: case new_connection_id: { CIDInfo* cidInfo = ssl->dtlsCidInfo; /* DTLS 1.3 only (RFC 9147) */ if (!ssl->options.dtls) { WOLFSSL_MSG("CID message received but not DTLS"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } /* RFC 9147 Section 9: if CIDs were not negotiated, MUST abort * with an unexpected_message alert */ if (cidInfo == NULL || !cidInfo->negotiated) { WOLFSSL_MSG("CID message received but CID not negotiated"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } if (ssl->options.handShakeState != HANDSHAKE_DONE) { WOLFSSL_MSG("CID message received out of order"); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (type == request_connection_id) { /* the peer MUST NOT request CIDs while sending an empty * CID itself */ if (cidInfo->rx == NULL || cidInfo->rx->length == 0) { WOLFSSL_MSG("RequestConnectionId from peer sending an " "empty CID"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } } else { /* the peer MUST NOT issue CIDs after negotiating receiving * an empty CID */ if (cidInfo->tx == NULL || cidInfo->tx->length == 0) { WOLFSSL_MSG("NewConnectionId from peer that negotiated " "an empty CID"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } } break; } #endif /* WOLFSSL_DTLS13 && WOLFSSL_DTLS_CID */ default: WOLFSSL_MSG("Unknown message type"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; } return 0; } /* Handle a type of handshake message that has been received. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the buffer of the current message. * On exit, the index into the buffer of the next message. * size The length of the current handshake message. * totalSz Length of remaining data in the message buffer. * returns 0 on success and otherwise failure. */ /* Run the key schedule belonging to a just-processed handshake message. * A pend here cannot replay the message (its handler already advanced * state); the record is consumed and this is called again from * DoProcessReplyEx() entry, pre-dispatch, or the reply-loop exits. * Returns 0, WC_PENDING_E while the device is busy, else an error. */ int DoTls13MsgDerives(WOLFSSL* ssl, byte type) { int ret = 0; (void)ssl; (void)type; #ifndef NO_WOLFSSL_CLIENT if (ssl->options.side == WOLFSSL_CLIENT_END) { if (type == server_hello) { /* Entered before the first derive, or a pend there would * route the retry back to the message handler. */ if (ssl->kdfMsgStep == TLS13_MSG_KDF_NONE) { ssl->kdfMsgStep = TLS13_MSG_KDF_SH_ENTERED; ssl->kdfMsgType = type; } if (ssl->kdfMsgStep <= TLS13_MSG_KDF_SH_ENTERED) { ret = DeriveEarlySecret(ssl); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_SH_EARLY_SECRET; } if (ssl->kdfMsgStep <= TLS13_MSG_KDF_SH_EARLY_SECRET) { ret = DeriveHandshakeSecret(ssl); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_SH_HS_SECRET; } if (ssl->kdfMsgStep <= TLS13_MSG_KDF_SH_HS_SECRET) { ret = DeriveTls13Keys(ssl, handshake_key, ENCRYPT_AND_DECRYPT_SIDE, 1); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_SH_HS_KEYS; } #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data) { if (ssl->kdfMsgStep <= TLS13_MSG_KDF_SH_HS_KEYS) { ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_SH_KEYS_SET; } } else #endif { if (ssl->kdfMsgStep <= TLS13_MSG_KDF_SH_HS_KEYS) { ret = SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_SH_KEYS_SET; } } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { w64wrapper epochHandshake; epochHandshake = w64From32(0, DTLS13_EPOCH_HANDSHAKE); ssl->dtls13Epoch = epochHandshake; ssl->dtls13PeerEpoch = epochHandshake; if (ssl->kdfMsgStep <= TLS13_MSG_KDF_SH_KEYS_SET) { ret = Dtls13SetEpochKeys(ssl, epochHandshake, ENCRYPT_AND_DECRYPT_SIDE); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_SH_DTLS_EPOCH; } } #endif /* WOLFSSL_DTLS13 */ ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; } if (type == finished) { /* Mark the phase entered before the first derive, so a pending * there still routes the retry back here. */ if (ssl->kdfMsgStep == TLS13_MSG_KDF_NONE) { ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_ENTERED; ssl->kdfMsgType = type; } if (ssl->kdfMsgStep <= TLS13_MSG_KDF_FIN_ENTERED) { if ((ret = DeriveMasterSecret(ssl)) != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } /* Zeroized only after the derive completed: a pend retry * still reads preMasterSecret. */ ForceZero(ssl->arrays->preMasterSecret, MAX_PREMASTER_SZ); ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_MASTER_SECRET; } #ifdef WOLFSSL_EARLY_DATA #ifdef WOLFSSL_QUIC if (ssl->kdfMsgStep <= TLS13_MSG_KDF_FIN_MASTER_SECRET) { if (WOLFSSL_IS_QUIC(ssl) && ssl->earlyData != no_early_data) { /* QUIC never sends/receives EndOfEarlyData, but * having early data means the last encryption keys * had not been set yet. */ if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } } ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_QUIC_EARLY_KEYS; } #endif if (ssl->kdfMsgStep <= TLS13_MSG_KDF_FIN_QUIC_EARLY_KEYS) { ret = DeriveTls13Keys(ssl, traffic_key, ENCRYPT_AND_DECRYPT_SIDE, ssl->earlyData == no_early_data); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_TRAFFIC_KEYS; } if (ssl->earlyData != no_early_data) { if (ssl->kdfMsgStep <= TLS13_MSG_KDF_FIN_TRAFFIC_KEYS) { if ((ret = DeriveTls13Keys(ssl, no_key, DECRYPT_SIDE_ONLY, 1)) != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_TRAFFIC_DONE; } } #else if (ssl->kdfMsgStep <= TLS13_MSG_KDF_FIN_QUIC_EARLY_KEYS) { ret = DeriveTls13Keys(ssl, traffic_key, ENCRYPT_AND_DECRYPT_SIDE, 1); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_TRAFFIC_DONE; } #endif /* Setup keys for application data messages. */ if (ssl->kdfMsgStep <= TLS13_MSG_KDF_FIN_TRAFFIC_DONE) { if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_KEYS_SET; } ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; } } #endif /* NO_WOLFSSL_CLIENT */ #ifndef NO_WOLFSSL_SERVER #if defined(HAVE_SESSION_TICKET) if (ssl->options.side == WOLFSSL_SERVER_END && type == finished) { if (ssl->kdfMsgStep == TLS13_MSG_KDF_NONE) { ssl->kdfMsgStep = TLS13_MSG_KDF_SFIN_ENTERED; ssl->kdfMsgType = type; } if (ssl->kdfMsgStep <= TLS13_MSG_KDF_SFIN_ENTERED) { ret = DeriveResumptionSecret(ssl, ssl->session->masterSecret); if (ret != 0) { if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; return ret; } ssl->kdfMsgStep = TLS13_MSG_KDF_SFIN_RESUMPTION_SECRET; } ssl->kdfMsgStep = TLS13_MSG_KDF_NONE; } #endif #endif /* NO_WOLFSSL_SERVER */ return ret; } int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, byte type, word32 size, word32 totalSz) { int ret = 0, tmp; word32 inIdx = *inOutIdx; int alertType; int skipSanity = 0; #if defined(HAVE_ECH) && !defined(NO_WOLFSSL_SERVER) TLSX* echX = NULL; word32 echInOutIdx; #endif (void)totalSz; WOLFSSL_ENTER("DoTls13HandShakeMsgType"); /* make sure we can read the message */ if (*inOutIdx + size > totalSz) return INCOMPLETE_DATA; #ifdef WOLFSSL_ASYNC_CRYPT /* A message being replayed after its own handler pended has already * passed its sanity check and advanced the got_* state on the first * pass; the handler restores its cleared marker on completion. Consume * the one-shot marker here so only that replayed message skips the * check - messages dispatched after it (ssl->error may still read * WC_PENDING_E from a trailing key-schedule pend) are still checked. */ skipSanity = ssl->options.asyncReplayMsg; ssl->options.asyncReplayMsg = 0; #endif /* Sanity check msg received. Skipped on a WC_PENDING_E resume (it * would reject the replay against already-advanced state); handlers * restore their cleared got_* markers on completion instead. */ if (!skipSanity && (ret = SanityCheckTls13MsgReceived(ssl, type)) != 0) { WOLFSSL_MSG("Sanity Check on handshake message type received failed"); if (ret == WC_NO_ERR_TRACE(VERSION_ERROR)) SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version); else SendAlert(ssl, alert_fatal, unexpected_message); return ret; } #if defined(WOLFSSL_CALLBACKS) /* add name later, add on record and handshake header part back on */ if (ssl->toInfoOn) { ret = AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - HANDSHAKE_HEADER_SZ, size + HANDSHAKE_HEADER_SZ, READ_PROTO, RECORD_HEADER_SZ, ssl->heap); if (ret != 0) return ret; AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); } #endif if (ssl->options.handShakeState == HANDSHAKE_DONE && type != session_ticket && type != certificate_request && type != certificate && type != key_update && type != finished #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) && type != request_connection_id && type != new_connection_id #endif ) { WOLFSSL_MSG("HandShake message after handshake complete"); SendAlert(ssl, alert_fatal, unexpected_message); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.serverState == NULL_STATE && type != server_hello && type != hello_retry_request #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_NO_TLS12) && (!ssl->options.dtls || type != hello_verify_request) #endif /* defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_NO_TLS12) */ ) { WOLFSSL_MSG("First server message not server hello"); SendAlert(ssl, alert_fatal, unexpected_message); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } if (ssl->options.side == WOLFSSL_SERVER_END && ssl->options.clientState == NULL_STATE && type != client_hello) { WOLFSSL_MSG("First client message not client hello"); SendAlert(ssl, alert_fatal, unexpected_message); WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); return OUT_OF_ORDER_E; } /* above checks handshake state */ /* Finish an earlier message's key schedule before this message is * judged: it installs state this one is checked against. */ if (ssl->kdfMsgStep > 0) { ret = DoTls13MsgDerives(ssl, ssl->kdfMsgType); if (ret != 0) return ret; /* A resolved pend must not suppress the next sanity check. */ if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) { ssl->error = 0; } } switch (type) { #ifndef NO_WOLFSSL_CLIENT /* Messages only received by client. */ case server_hello: WOLFSSL_MSG("processing server hello"); ret = DoTls13ServerHello(ssl, input, inOutIdx, size, &type); #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \ ((defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \ (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH))) if (ssl->options.resuming || !IsAtLeastTLSv1_2(ssl) || IsAtLeastTLSv1_3(ssl->version)) { ssl->options.cacheMessages = 0; if ((ssl->hsHashes != NULL) && (ssl->hsHashes->messages != NULL)) { ForceZero(ssl->hsHashes->messages, ssl->hsHashes->length); XFREE(ssl->hsHashes->messages, ssl->heap, DYNAMIC_TYPE_HASHES); ssl->hsHashes->messages = NULL; } } #endif break; case encrypted_extensions: WOLFSSL_MSG("processing encrypted extensions"); ret = DoTls13EncryptedExtensions(ssl, input, inOutIdx, size); break; #ifndef NO_CERTS case certificate_request: WOLFSSL_MSG("processing certificate request"); ret = DoTls13CertificateRequest(ssl, input, inOutIdx, size); break; #endif case session_ticket: WOLFSSL_MSG("processing new session ticket"); ret = DoTls13NewSessionTicket(ssl, input, inOutIdx, size); break; #endif /* !NO_WOLFSSL_CLIENT */ #ifndef NO_WOLFSSL_SERVER /* Messages only received by server. */ case client_hello: WOLFSSL_MSG("processing client hello"); #if defined(HAVE_ECH) /* keep the start idx so we can restore it for the inner call */ echInOutIdx = *inOutIdx; #endif ret = DoTls13ClientHello(ssl, input, inOutIdx, size); #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \ ((defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \ (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH))) if ((ssl->options.resuming || !ssl->options.verifyPeer || !IsAtLeastTLSv1_2(ssl) || IsAtLeastTLSv1_3(ssl->version)) #ifdef WOLFSSL_DTLS13 && (!ssl->options.dtls) #endif ) { #ifdef WOLFSSL_HAVE_HS_SUSPEND if (!IsHsSuspendErr(ret)) #endif { ssl->options.cacheMessages = 0; if ((ssl->hsHashes != NULL) && (ssl->hsHashes->messages != NULL)) { ForceZero(ssl->hsHashes->messages, ssl->hsHashes->length); XFREE(ssl->hsHashes->messages, ssl->heap, DYNAMIC_TYPE_HASHES); ssl->hsHashes->messages = NULL; } } } #endif #if defined(HAVE_ECH) if (ret == 0) { echX = TLSX_Find(ssl->extensions, TLSX_ECH); if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE && ((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { byte copyRandom = ((WOLFSSL_ECH*)echX->data)->innerCount == 0; /* reset the inOutIdx to the outer start */ *inOutIdx = echInOutIdx; /* call again with the inner hello */ if (ret == 0) { echInOutIdx = HANDSHAKE_HEADER_SZ; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) echInOutIdx = DTLS13_HANDSHAKE_HEADER_SZ; #endif ssl->options.echProcessingInner = 1; ret = DoTls13ClientHello(ssl, ((WOLFSSL_ECH*)echX->data)->innerClientHello, &echInOutIdx, ((WOLFSSL_ECH*)echX->data)->innerClientHelloLen); ssl->options.echProcessingInner = 0; } if (ret == 0 && ((WOLFSSL_ECH*)echX->data)->state != ECH_PARSED_INTERNAL) { WOLFSSL_MSG("ECH: inner ClientHello missing ECH extension"); ret = INVALID_PARAMETER; } /* if the inner ech parsed successfully we have successfully * handled the hello and can skip the whole message */ if (ret == 0) { /* Copy inner client random for ECH acceptance calculation. * Only on first inner ClientHello (before HRR), not CH2. */ if (copyRandom) { XMEMCPY(ssl->arrays->clientRandomInner, ssl->arrays->clientRandom, RAN_LEN); } *inOutIdx += size; } } } #endif /* HAVE_ECH */ break; #ifdef WOLFSSL_EARLY_DATA case end_of_early_data: WOLFSSL_MSG("processing end of early data"); ret = DoTls13EndOfEarlyData(ssl, input, inOutIdx, size); break; #endif #endif /* !NO_WOLFSSL_SERVER */ /* Messages received by both client and server. */ #if !defined(NO_CERTS) && (!defined(NO_WOLFSSL_CLIENT) || \ !defined(WOLFSSL_NO_CLIENT_AUTH)) case certificate: WOLFSSL_MSG("processing certificate"); ret = DoTls13Certificate(ssl, input, inOutIdx, size); break; #endif #if (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_ED448) || defined(HAVE_FALCON) || \ defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA)) && \ !defined(NO_CERTS) case certificate_verify: WOLFSSL_MSG("processing certificate verify"); ret = DoTls13CertificateVerify(ssl, input, inOutIdx, size); break; #endif case finished: WOLFSSL_MSG("processing finished"); ret = DoTls13Finished(ssl, input, inOutIdx, size, totalSz, NO_SNIFF); break; case key_update: WOLFSSL_MSG("processing key update"); ret = DoTls13KeyUpdate(ssl, input, inOutIdx, size); break; #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) case request_connection_id: WOLFSSL_MSG("processing request connection id"); ret = DoDtls13RequestConnectionId(ssl, input, inOutIdx, size); break; case new_connection_id: WOLFSSL_MSG("processing new connection id"); ret = DoDtls13NewConnectionId(ssl, input, inOutIdx, size); break; #endif /* WOLFSSL_DTLS13 && WOLFSSL_DTLS_CID */ #if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) case hello_verify_request: WOLFSSL_MSG("processing hello verify request"); ret = DoHelloVerifyRequest(ssl, input, inOutIdx, size); break; #endif default: WOLFSSL_MSG("Unknown handshake message type"); ret = UNKNOWN_HANDSHAKE_TYPE; break; } #ifdef WOLFSSL_ASYNC_CRYPT /* Handler pended: this exact message will be replayed. Mark it so the * replay skips its sanity check (it already passed and advanced state). * Set from the handler's own ret, before the trailing key-schedule * derive below can re-raise WC_PENDING_E for an already-done message. */ if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) ssl->options.asyncReplayMsg = 1; #endif #ifdef WOLFSSL_HAVE_HS_SUSPEND /* if suspended, offset index so this msg will be processed again */ /* NOTE: check this now before other calls can overwrite ret */ if (IsHsSuspendErr(ret) && *inOutIdx > 0) { /* DTLS always stores a message in a buffer when async is enable, so we * don't need to adjust for the extra bytes here (*inOutIdx is always * == 0) */ *inOutIdx -= HANDSHAKE_HEADER_SZ; } /* make sure suspend error is cleared */ if (ret == 0 && IsHsSuspendErr(ssl->error)) { ssl->error = 0; } #endif #if defined(HAVE_WRITE_DUP) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) /* Read side: a fresh PHA CertificateRequest. Resume from the transcript * the write side published after the previous PHA response, so this CR is * hashed onto the same base the server has. */ if (ret == 0 && type == certificate_request && ssl->options.side == WOLFSSL_CLIENT_END && ssl->dupSide == READ_DUP_SIDE && ssl->options.handShakeState == HANDSHAKE_DONE && ssl->dupWrite != NULL) { if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) return BAD_MUTEX_E; if (ssl->dupWrite->postHandshakeSyncedHashState != NULL) { FreeHandshakeHashes(ssl); ssl->hsHashes = ssl->dupWrite->postHandshakeSyncedHashState; ssl->dupWrite->postHandshakeSyncedHashState = NULL; } wc_UnLockMutex(&ssl->dupWrite->dupMutex); } #endif /* HAVE_WRITE_DUP && WOLFSSL_POST_HANDSHAKE_AUTH */ if (ret == 0 && type != client_hello && type != session_ticket && type != key_update #if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CID) && type != request_connection_id && type != new_connection_id #endif ) { ret = HashInput(ssl, input + inIdx, (int)size); } alertType = TranslateErrorToAlert(ret); /* Skip when a fatal alert already went out for this error. The message * handlers reached above send their own, more specific alert before * returning (a protocol_version from the version checks in DoClientHello, * decode_error, illegal_parameter, inappropriate_fallback), and a second * fatal alert on a connection that is already being torn down is not a * message the peer can act on. Matches the guard in * SendFatalAlertOnly(). */ if (alertType != invalid_alert && ssl->alert_history.last_tx.level != alert_fatal) { #ifdef WOLFSSL_DTLS13 if (type == client_hello && ssl->options.dtls) DtlsSetSeqNumForReply(ssl); #endif tmp = SendAlert(ssl, alert_fatal, alertType); /* propagate socket error instead of tls error to be sure the error is * not ignored by DTLS code */ if (tmp == WC_NO_ERR_TRACE(SOCKET_ERROR_E)) ret = SOCKET_ERROR_E; } if (ret == 0 && ssl->options.tls1_3) { /* The message is hashed by now; run the key schedule that belongs to * it. */ ret = DoTls13MsgDerives(ssl, type); if (ret != 0) return ret; #if !defined(NO_WOLFSSL_CLIENT) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) if (ssl->options.side == WOLFSSL_CLIENT_END) { if (type == certificate_request && ssl->options.handShakeState == HANDSHAKE_DONE) { #if defined(HAVE_WRITE_DUP) /* Read side cannot write; delegate the cert response to the * write side by saving auth state in the shared WriteDup. */ if (ssl->dupSide == READ_DUP_SIDE) { if (ssl->dupWrite == NULL) return BAD_STATE_E; if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) return BAD_MUTEX_E; /* Copy the current transcript so the write side can * compute the correct Finished MAC. */ ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, &ssl->dupWrite->postHandshakeHashState); if (ret == 0) { /* Copy the cert request context. */ CertReqCtx** tail = &ssl->certReqCtx; while (*tail != NULL) tail = &(*tail)->next; *tail = ssl->dupWrite->postHandshakeCertReqCtx; ssl->dupWrite->postHandshakeCertReqCtx = ssl->certReqCtx; ssl->certReqCtx = NULL; ssl->dupWrite->postHandshakeSendVerify = ssl->options.sendVerify; ssl->dupWrite->postHandshakeSigAlgo = ssl->options.sigAlgo; ssl->dupWrite->postHandshakeHashAlgo = ssl->options.hashAlgo; #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) /* The request just parsed decides whether the chain * about to be sent may be SHA-1 signed. */ ssl->dupWrite->postHandshakeSha1CertOk = (byte)ssl->options.peerSha1CertOk; #endif ssl->dupWrite->postHandshakeAuthPending = 1; } wc_UnLockMutex(&ssl->dupWrite->dupMutex); /* Leave ssl->options unchanged: read side must not reset * its states or call wolfSSL_connect_TLSv13. */ } else #endif /* HAVE_WRITE_DUP */ { /* reset handshake states */ ssl->options.clientState = CLIENT_HELLO_COMPLETE; ssl->options.connectState = FIRST_REPLY_DONE; ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; ssl->options.processReply = 0; /* doProcessInit */ /* DTLSv1.3 note: We can't reset serverState to SERVER_FINISHED_COMPLETE with the goal that this connect blocks until the cert/cert_verify/finished flight gets ACKed by the server. The problem is that we will invoke ProcessReplyEx() in that case, but we came here from ProcessReplyEx() and it is not re-entrant safe (the input buffer would still have the certificate_request message). */ if (wolfSSL_connect_TLSv13(ssl) != WOLFSSL_SUCCESS) { ret = ssl->error; if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) ret = POST_HAND_AUTH_ERROR; } } } } #endif /* !NO_WOLFSSL_CLIENT && WOLFSSL_POST_HANDSHAKE_AUTH */ } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && !ssl->options.dtlsStateful) { DtlsResetState(ssl); if (DtlsIgnoreError(ret)) ret = 0; } #endif WOLFSSL_LEAVE("DoTls13HandShakeMsgType()", ret); return ret; } /* Handle a handshake message that has been received. * * ssl The SSL/TLS object. * input The message buffer. * inOutIdx On entry, the index into the buffer of the current message. * On exit, the index into the buffer of the next message. * totalSz Length of remaining data in the message buffer. * returns 0 on success and otherwise failure. */ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 totalSz) { int ret = 0; word32 inputLength; byte type; word32 size = 0; #ifdef WOLFSSL_HAVE_HS_SUSPEND /* Nonzero on entry: an earlier message's schedule is unfinished, so a * pend from its pre-dispatch drain must re-present this message. */ byte kdfStepEntry = ssl->kdfMsgStep; #endif WOLFSSL_ENTER("DoTls13HandShakeMsg"); /* totalSz is now curStartIdx + curSize (content-only, padSz already * subtracted in ProcessReply). */ if (*inOutIdx > totalSz) return BUFFER_ERROR; inputLength = totalSz - *inOutIdx; /* If there is a pending fragmented handshake message, * pending message size will be non-zero. */ if (ssl->pendingMsgSz == 0) { #ifdef WOLFSSL_HAVE_HS_SUSPEND word32 startIdx = *inOutIdx; #endif if (GetHandshakeHeader(ssl, input, inOutIdx, &type, &size, totalSz) != 0) { WOLFSSL_ERROR_VERBOSE(PARSE_ERROR); return PARSE_ERROR; } ret = EarlySanityCheckMsgReceived(ssl, type, (inputLength > HANDSHAKE_HEADER_SZ) ? min(inputLength - HANDSHAKE_HEADER_SZ, size) : 0); if (ret != 0) { WOLFSSL_ERROR(ret); return ret; } /* Cap the maximum size of a handshake message to something reasonable. * By default is the maximum size of a certificate message assuming * nine 2048-bit RSA certificates in the chain. */ if (size > MAX_HANDSHAKE_SZ) { WOLFSSL_MSG("Handshake message too large"); WOLFSSL_ERROR_VERBOSE(HANDSHAKE_SIZE_ERROR); return HANDSHAKE_SIZE_ERROR; } /* size is the size of the certificate message payload */ if (inputLength - HANDSHAKE_HEADER_SZ < size) { /* Commit pending state only after the allocation succeeds. */ ssl->pendingMsg = (byte*)XMALLOC(size + HANDSHAKE_HEADER_SZ, ssl->heap, DYNAMIC_TYPE_ARRAYS); if (ssl->pendingMsg == NULL) return MEMORY_E; ssl->pendingMsgType = type; ssl->pendingMsgSz = size + HANDSHAKE_HEADER_SZ; XMEMCPY(ssl->pendingMsg, input + *inOutIdx - HANDSHAKE_HEADER_SZ, inputLength); ssl->pendingMsgOffset = inputLength; *inOutIdx += inputLength - HANDSHAKE_HEADER_SZ; return 0; } ret = DoTls13HandShakeMsgType(ssl, input, inOutIdx, type, size, totalSz); #ifdef WOLFSSL_HAVE_HS_SUSPEND if (IsHsSuspendErr(ret) && (ret != WC_NO_ERR_TRACE(WC_PENDING_E) || ((ssl->kdfMsgStep == 0 || kdfStepEntry != 0) && ssl->options.processReply != 0 /* doProcessInit */))) { /* Re-present for in-handler pends and pre-dispatch drain pends. * Not for post-handler pends, which committed the message: a * key-schedule pend (kdfMsgStep != 0) resumes through * DoTls13MsgDerives(), and a post-handshake-auth send pend * (processReply reset to doProcessInit) resumes through * wolfSSL_negotiate(). */ *inOutIdx = startIdx; } #endif } else { if (inputLength + ssl->pendingMsgOffset > ssl->pendingMsgSz) { inputLength = ssl->pendingMsgSz - ssl->pendingMsgOffset; } ret = EarlySanityCheckMsgReceived(ssl, ssl->pendingMsgType, inputLength); if (ret != 0) { WOLFSSL_ERROR(ret); return ret; } XMEMCPY(ssl->pendingMsg + ssl->pendingMsgOffset, input + *inOutIdx, inputLength); ssl->pendingMsgOffset += inputLength; *inOutIdx += inputLength; if (ssl->pendingMsgOffset == ssl->pendingMsgSz) { word32 idx = 0; ret = DoTls13HandShakeMsgType(ssl, ssl->pendingMsg + HANDSHAKE_HEADER_SZ, &idx, ssl->pendingMsgType, ssl->pendingMsgSz - HANDSHAKE_HEADER_SZ, ssl->pendingMsgSz); #ifdef WOLFSSL_HAVE_HS_SUSPEND if (IsHsSuspendErr(ret) && (ret != WC_NO_ERR_TRACE(WC_PENDING_E) || ((ssl->kdfMsgStep == 0 || kdfStepEntry != 0) && ssl->options.processReply != 0 /* doProcessInit */))) { /* Re-present the fragment; a post-handler pend falls * through and consumes the message. */ ssl->pendingMsgOffset -= inputLength; *inOutIdx -= inputLength; } else #endif { XFREE(ssl->pendingMsg, ssl->heap, DYNAMIC_TYPE_ARRAYS); ssl->pendingMsg = NULL; ssl->pendingMsgSz = 0; } } } WOLFSSL_LEAVE("DoTls13HandShakeMsg", ret); return ret; } #ifndef NO_WOLFSSL_CLIENT /* The client connecting to the server. * The protocol version is expecting to be TLS v1.3. * If the server downgrades, and older versions of the protocol are compiled * in, the client will fallback to wolfSSL_connect(). * Please see note at top of README if you get an error from connect. * * ssl The SSL/TLS object. * returns WOLFSSL_SUCCESS on successful handshake, WOLFSSL_FATAL_ERROR when * unrecoverable error occurs and 0 otherwise. * For more error information use wolfSSL_get_error(). */ int wolfSSL_connect_TLSv13(WOLFSSL* ssl) { int advanceState; int ret = 0; WOLFSSL_ENTER("wolfSSL_connect_TLSv13"); #ifdef HAVE_ERRNO_H errno = 0; #endif if (ssl == NULL) return BAD_FUNC_ARG; if (ssl->options.side != WOLFSSL_CLIENT_END) { ssl->error = SIDE_ERROR; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } /* make sure this wolfSSL object has arrays and rng setup. Protects * case where the WOLFSSL object is reused via wolfSSL_clear() */ if ((ret = ReinitSSL(ssl, ssl->ctx, 0)) != 0) { return ret; } #ifdef WOLFSSL_DTLS if (ssl->version.major == DTLS_MAJOR) { ssl->options.dtls = 1; ssl->options.dtlsStateful = 1; } #endif #ifdef WOLFSSL_WOLFSENTRY_HOOKS if ((ssl->ConnectFilter != NULL) && (ssl->options.connectState == CONNECT_BEGIN)) { wolfSSL_netfilter_decision_t res; if ((ssl->ConnectFilter(ssl, ssl->ConnectFilter_arg, &res) == WOLFSSL_SUCCESS) && (res == WOLFSSL_NETFILTER_REJECT)) { ssl->error = SOCKET_FILTERED_E; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_WOLFSENTRY_HOOKS */ /* fragOffset is non-zero when sending fragments. On the last * fragment, fragOffset is zero again, and the state can be * advanced. Also, only advance from states in which we send data */ advanceState = (ssl->options.connectState == CONNECT_BEGIN || ssl->options.connectState == HELLO_AGAIN || (ssl->options.connectState >= FIRST_REPLY_DONE && ssl->options.connectState <= FIRST_REPLY_FOURTH)); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) advanceState = advanceState && !ssl->dtls13SendingFragments && !ssl->dtls13SendingAckOrRtx; #endif /* WOLFSSL_DTLS13 */ if (ssl->buffers.outputBuffer.length > 0 #ifdef WOLFSSL_HAVE_HS_SUSPEND /* do not send buffered or advance state if the last error suspended the handshake - advancing frees the saved state */ && !IsHsSuspendErr(ssl->error) #endif ) { if ((ret = SendBuffered(ssl)) == 0) { if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) { if (advanceState) { #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version) && ssl->options.connectState == FIRST_REPLY_FOURTH) { /* WAIT_FINISHED_ACK is a state added afterwards, but it can't follow FIRST_REPLY_FOURTH in the enum order. Indeed the value of the enum ConnectState is stored in serialized session. This would make importing serialized session from other wolfSSL version incompatible */ ssl->options.connectState = WAIT_FINISHED_ACK; } else #endif /* WOLFSSL_DTLS13 */ { ssl->options.connectState++; } WOLFSSL_MSG("connect state: " "Advanced from last buffered fragment send"); #ifdef WOLFSSL_ASYNC_IO FreeAsyncCtx(ssl, 0); #endif } } else { WOLFSSL_MSG("connect state: " "Not advanced, more fragments to send"); } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) ssl->dtls13SendingAckOrRtx = 0; #endif /* WOLFSSL_DTLS13 */ } else { ssl->error = ret; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ret = RetrySendAlert(ssl); if (ret != 0) { ssl->error = ret; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && ssl->dtls13SendingFragments) { if ((ssl->error = Dtls13FragmentsContinue(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } /* we sent all the fragments. Advance state. */ ssl->options.connectState++; } #endif /* WOLFSSL_DTLS13 */ switch (ssl->options.connectState) { case CONNECT_BEGIN: /* Always send client hello first. */ if ((ssl->error = SendTls13ClientHello(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } ssl->options.connectState = CLIENT_HELLO_SENT; WOLFSSL_MSG("TLSv13 connect state: CLIENT_HELLO_SENT"); FALL_THROUGH; case CLIENT_HELLO_SENT: #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data && ssl->options.handShakeState != CLIENT_HELLO_COMPLETE) { #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) if (!ssl->options.dtls && !ssl->options.sentChangeCipher && ssl->options.tls13MiddleBoxCompat) { ssl->error = SendChangeCipher(ssl); /* A short send leaves the record queued in the output * buffer, so a resumed connect must not build a second * one. Same on every other site. */ if (ssl->error == 0 || ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) { ssl->options.sentChangeCipher = 1; } if (ssl->error != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; return WOLFSSL_SUCCESS; } #endif /* Get the response/s from the server. */ while (ssl->options.serverState < SERVER_HELLOVERIFYREQUEST_COMPLETE) { if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_DTLS13 */ } if (!ssl->options.tls1_3) { #ifndef WOLFSSL_NO_TLS12 if (ssl->options.downgrade) return wolfSSL_connect(ssl); #endif WOLFSSL_MSG("Client using higher version, fatal error"); WOLFSSL_ERROR_VERBOSE(VERSION_ERROR); return VERSION_ERROR; } ssl->options.connectState = HELLO_AGAIN; WOLFSSL_MSG("connect state: HELLO_AGAIN"); FALL_THROUGH; case HELLO_AGAIN: if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) { #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) if (!ssl->options.dtls && !ssl->options.sentChangeCipher && ssl->options.tls13MiddleBoxCompat) { ssl->error = SendChangeCipher(ssl); if (ssl->error == 0 || ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) { ssl->options.sentChangeCipher = 1; } if (ssl->error != 0) { /* The second ClientHello still has to follow, so hold * the state machine on this case while the record * drains. */ ssl->options.buildingMsg = 1; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* Try again with different security parameters. */ if ((ssl->error = SendTls13ClientHello(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ssl->options.connectState = HELLO_AGAIN_REPLY; WOLFSSL_MSG("connect state: HELLO_AGAIN_REPLY"); FALL_THROUGH; case HELLO_AGAIN_REPLY: /* Get the response/s from the server. */ while (ssl->options.serverState < SERVER_FINISHED_COMPLETE) { #ifdef WOLFSSL_DTLS13 if (!IsAtLeastTLSv1_3(ssl->version)) { #ifndef WOLFSSL_NO_TLS12 if (ssl->options.downgrade) return wolfSSL_connect(ssl); #endif } #endif /* WOLFSSL_DTLS13 */ if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_DTLS13 */ } /* Finish a key schedule the Finished handler left pending: * it must complete before this side's sends advance the * transcript the traffic secrets hash. */ if (ssl->kdfMsgStep > 0) { ssl->error = DoTls13MsgDerives(ssl, ssl->kdfMsgType); if (ssl->error != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ssl->options.connectState = FIRST_REPLY_DONE; WOLFSSL_MSG("connect state: FIRST_REPLY_DONE"); FALL_THROUGH; case FIRST_REPLY_DONE: if (ssl->options.certOnly) return WOLFSSL_SUCCESS; #ifdef WOLFSSL_EARLY_DATA /* Not on the post-handshake auth re-entry: a second * EndOfEarlyData restarts the write sequence number at 0. */ if (!ssl->options.dtls && ssl->earlyData != no_early_data && !TLS13_AFTER_HANDSHAKE(ssl) && !WOLFSSL_IS_QUIC(ssl)) { if ((ssl->error = SendTls13EndOfEarlyData(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } WOLFSSL_MSG("sent: end_of_early_data"); } #endif ssl->options.connectState = FIRST_REPLY_FIRST; WOLFSSL_MSG("connect state: FIRST_REPLY_FIRST"); FALL_THROUGH; case FIRST_REPLY_FIRST: #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) if (!ssl->options.sentChangeCipher && !ssl->options.dtls && ssl->options.tls13MiddleBoxCompat) { ssl->error = SendChangeCipher(ssl); if (ssl->error == 0 || ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) { ssl->options.sentChangeCipher = 1; } if (ssl->error != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif ssl->options.connectState = FIRST_REPLY_SECOND; WOLFSSL_MSG("connect state: FIRST_REPLY_SECOND"); FALL_THROUGH; case FIRST_REPLY_SECOND: /* CLIENT: check peer authentication. */ if (!ssl->options.peerAuthGood) { WOLFSSL_MSG("Server authentication did not happen"); WOLFSSL_ERROR_VERBOSE(WOLFSSL_FATAL_ERROR); return WOLFSSL_FATAL_ERROR; } #ifndef NO_CERTS if ((!ssl->options.resuming || TLS13_AFTER_HANDSHAKE(ssl)) && ssl->options.sendVerify) { ssl->error = SendTls13Certificate(ssl); if (ssl->error != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } WOLFSSL_MSG("sent: certificate"); } #endif ssl->options.connectState = FIRST_REPLY_THIRD; WOLFSSL_MSG("connect state: FIRST_REPLY_THIRD"); FALL_THROUGH; case FIRST_REPLY_THIRD: #if (!defined(NO_CERTS) && (!defined(NO_RSA) || defined(HAVE_ECC) || \ defined(HAVE_ED25519) || defined(HAVE_ED448) || \ defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ defined(WOLFSSL_HAVE_SLHDSA))) && \ (!defined(NO_WOLFSSL_SERVER) || !defined(WOLFSSL_NO_CLIENT_AUTH)) if ((!ssl->options.resuming || TLS13_AFTER_HANDSHAKE(ssl)) && ssl->options.sendVerify) { ssl->error = SendTls13CertificateVerify(ssl); if (ssl->error != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } WOLFSSL_MSG("sent: certificate verify"); } #endif ssl->options.connectState = FIRST_REPLY_FOURTH; WOLFSSL_MSG("connect state: FIRST_REPLY_FOURTH"); FALL_THROUGH; case FIRST_REPLY_FOURTH: if ((ssl->error = SendTls13Finished(ssl)) != 0) { wolfssl_local_MaybeCheckAlertOnErr(ssl, ssl->error); WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } WOLFSSL_MSG("sent: finished"); #ifdef WOLFSSL_DTLS13 ssl->options.connectState = WAIT_FINISHED_ACK; WOLFSSL_MSG("connect state: WAIT_FINISHED_ACK"); FALL_THROUGH; case WAIT_FINISHED_ACK: if (ssl->options.dtls) { while (ssl->options.serverState != SERVER_FINISHED_ACKED) { if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } } #endif /* WOLFSSL_DTLS13 */ ssl->options.connectState = FINISHED_DONE; WOLFSSL_MSG("connect state: FINISHED_DONE"); FALL_THROUGH; case FINISHED_DONE: #ifndef NO_HANDSHAKE_DONE_CB if (ssl->hsDoneCb != NULL) { int cbret = ssl->hsDoneCb(ssl, ssl->hsDoneCtx); if (cbret < 0) { ssl->error = cbret; WOLFSSL_ERROR_VERBOSE(ssl->error); WOLFSSL_MSG("HandShake Done Cb don't continue error"); return WOLFSSL_FATAL_ERROR; } } #endif /* NO_HANDSHAKE_DONE_CB */ #if defined(HAVE_ECH) /* RFC 9849 s6.1.6: if we offered ECH but the server rejected it, * send ech_required alert and abort before returning to the app */ if (ssl->echConfigs != NULL && !ssl->options.disableECH && !ssl->options.echAccepted) { if (ssl->echRetryConfigs != NULL) { ssl->options.echRetryConfigsAccepted = 1; } SendAlert(ssl, alert_fatal, ech_required); ssl->error = ECH_REQUIRED_E; WOLFSSL_ERROR_VERBOSE(ECH_REQUIRED_E); return WOLFSSL_FATAL_ERROR; } #endif /* HAVE_ECH */ if (!ssl->options.keepResources) { FreeHandshakeResources(ssl); } #if defined(WOLFSSL_ASYNC_IO) && !defined(WOLFSSL_ASYNC_CRYPT) /* Free the remaining async context if not using it for crypto */ FreeAsyncCtx(ssl, 1); #endif ssl->error = 0; /* clear the error */ WOLFSSL_LEAVE("wolfSSL_connect_TLSv13", WOLFSSL_SUCCESS); return WOLFSSL_SUCCESS; default: WOLFSSL_MSG("Unknown connect state ERROR"); return WOLFSSL_FATAL_ERROR; /* unknown connect state */ } } #endif #if defined(WOLFSSL_SEND_HRR_COOKIE) /* Send a cookie with the HelloRetryRequest to avoid storing state. * * ssl SSL/TLS object. * secret Secret to use when generating integrity check for cookie. * A value of NULL indicates to generate a new random secret. * secretSz Size of secret data in bytes. * Use a value of 0 to indicate use of default size. * returns BAD_FUNC_ARG when ssl is NULL or not using TLS v1.3, SIDE_ERROR when * called on a client, BAD_STATE_E when the DTLS handshake has already decided * how to process this ClientHello; WOLFSSL_SUCCESS on success and otherwise * failure. */ int wolfSSL_send_hrr_cookie(WOLFSSL* ssl, const unsigned char* secret, unsigned int secretSz) { #ifndef NO_WOLFSSL_SERVER int ret; #endif 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; ret = CheckCookieState(ssl); if (ret != WOLFSSL_SUCCESS) return ret; /* On failure the previous secrets and policy are kept. */ return CookiePolicySet(ssl, secret, secretSz, 1); #else (void)secret; (void)secretSz; return SIDE_ERROR; #endif } int wolfSSL_disable_hrr_cookie(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #ifdef NO_WOLFSSL_SERVER return SIDE_ERROR; #else return wolfSSL_disable_cookie(ssl); #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; } /* A NULL/empty secret just clears the secondary secret. */ if (secret == NULL || secretSz == 0) { FreeCookieSecret(ssl, &ssl->buffers.tls13CookieSecretSecondary); ret = WOLFSSL_SUCCESS; } else { /* The replacement is built before the old secret is dropped, so a * failed rotation leaves the secondary secret that was in use. */ ret = SetCookieSecret(ssl, &ssl->buffers.tls13CookieSecretSecondary, secret, secretSz, "wolfSSL_set_hrr_cookie_secret_secondary secret"); if (ret == 0) ret = WOLFSSL_SUCCESS; } #else (void)secret; (void)secretSz; ret = SIDE_ERROR; #endif return ret; } #endif /* defined(WOLFSSL_SEND_HRR_COOKIE) */ #ifdef HAVE_SUPPORTED_CURVES /* Create a key share entry from group. * Generates a key pair. * * ssl The SSL/TLS object. * group The named group. * returns 0 on success, otherwise failure. * for async can return WC_PENDING_E and should be called again */ int wolfSSL_UseKeyShare(WOLFSSL* ssl, word16 group) { int ret; if (ssl == NULL) return BAD_FUNC_ARG; #ifdef WOLFSSL_ASYNC_CRYPT ret = wolfSSL_AsyncPop(ssl, NULL); if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) { /* Check for error */ if (ret < 0) return ret; } #endif #if defined(WOLFSSL_HAVE_MLKEM) if (WOLFSSL_NAMED_GROUP_IS_PQC(group) || WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group)) { if (!IsAtLeastTLSv1_3(ssl->version)) { return BAD_FUNC_ARG; } if (ssl->options.side == WOLFSSL_SERVER_END) { /* If I am the server of a KEM connection, do not do keygen because * I'm going to encapsulate with the client's public key. Note that * I might be the client and ssl->option.side has not been properly * set yet. In that case the KeyGen operation will be deferred to * connection time. */ return WOLFSSL_SUCCESS; } } #endif #if defined(NO_TLS) (void)ret; (void)group; #else /* Check if the group is supported. */ if (!TLSX_IsGroupSupported(group, ssl->options.side)) { WOLFSSL_MSG("Group not supported."); return BAD_FUNC_ARG; } ret = TLSX_KeyShare_Use(ssl, group, 0, NULL, NULL, &ssl->extensions); if (ret != 0) return ret; #endif /* NO_TLS */ return WOLFSSL_SUCCESS; } /* Send no key share entries - use HelloRetryRequest to negotiate shared group. * * ssl The SSL/TLS object. * returns 0 on success, otherwise failure. */ int wolfSSL_NoKeyShares(WOLFSSL* ssl) { int ret; if (ssl == NULL) return BAD_FUNC_ARG; if (ssl->options.side == WOLFSSL_SERVER_END) return SIDE_ERROR; #if defined(NO_TLS) (void)ret; #else ret = TLSX_KeyShare_Empty(ssl); if (ret != 0) return ret; #endif /* NO_TLS */ return WOLFSSL_SUCCESS; } #endif #ifdef WOLFSSL_DUAL_ALG_CERTS int wolfSSL_UseCKS(WOLFSSL* ssl, byte *sigSpec, word16 sigSpecSz) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->ctx->method->version) || sigSpec == NULL || sigSpecSz == 0) return BAD_FUNC_ARG; ssl->sigSpec = sigSpec; ssl->sigSpecSz = sigSpecSz; return WOLFSSL_SUCCESS; } int wolfSSL_CTX_UseCKS(WOLFSSL_CTX* ctx, byte *sigSpec, word16 sigSpecSz) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version) || sigSpec == NULL || sigSpecSz == 0) return BAD_FUNC_ARG; ctx->sigSpec = sigSpec; ctx->sigSpecSz = sigSpecSz; return WOLFSSL_SUCCESS; } #endif /* WOLFSSL_DUAL_ALG_CERTS */ /* Do not send a ticket after TLS v1.3 handshake for resumption. * * ctx The SSL/TLS CTX object. * returns BAD_FUNC_ARG when ctx is NULL and 0 on success. */ int wolfSSL_CTX_no_ticket_TLSv13(WOLFSSL_CTX* ctx) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; if (ctx->method->side == WOLFSSL_CLIENT_END) return SIDE_ERROR; #ifdef HAVE_SESSION_TICKET ctx->noTicketTls13 = 1; #endif return 0; } /* Do not send a ticket after TLS v1.3 handshake for resumption. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL, not using TLS v1.3, or called on * a client and 0 on success. */ int wolfSSL_no_ticket_TLSv13(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; if (ssl->options.side == WOLFSSL_CLIENT_END) return SIDE_ERROR; #ifdef HAVE_SESSION_TICKET ssl->options.noTicketTls13 = 1; #endif return 0; } /* Disallow (EC)DHE key exchange when using pre-shared keys. * * ctx The SSL/TLS CTX object. * returns BAD_FUNC_ARG when ctx is NULL and 0 on success. */ int wolfSSL_CTX_no_dhe_psk(WOLFSSL_CTX* ctx) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ctx->noPskDheKe = 1; #endif return 0; } /* Disallow (EC)DHE key exchange when using pre-shared keys. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL, or not using TLS v1.3 and 0 on * success. */ int wolfSSL_no_dhe_psk(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ssl->options.noPskDheKe = 1; ssl->options.noPskDheKePolicy = 1; #endif return 0; } #ifdef HAVE_SUPPORTED_CURVES /* Only allow (EC)DHE key exchange when using pre-shared keys. * * ctx The SSL/TLS CTX object. * returns BAD_FUNC_ARG when ctx is NULL and 0 on success. */ int wolfSSL_CTX_only_dhe_psk(WOLFSSL_CTX* ctx) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ctx->onlyPskDheKe = 1; #endif return 0; } /* Only allow (EC)DHE key exchange when using pre-shared keys. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL, or not using TLS v1.3 and 0 on * success. */ int wolfSSL_only_dhe_psk(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ssl->options.onlyPskDheKe = 1; #endif return 0; } #endif /* HAVE_SUPPORTED_CURVES */ /* Require that an external Pre-Shared Key is negotiated for the handshake to * succeed. TLS 1.3 / DTLS 1.3 only - in (D)TLS 1.2 the use of a PSK is * determined by the negotiated cipher suite, so a mandatory PSK is configured * there by restricting the cipher suite list to PSK suites. * * ctx The SSL/TLS CTX object. * returns BAD_FUNC_ARG when ctx is NULL or not at least TLS v1.3, 0 on success. */ int wolfSSL_CTX_require_psk(WOLFSSL_CTX* ctx) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ctx->failNoPSK = 1; /* The requirement can only be enforced for (D)TLS 1.3, so keep it * fail-closed by disabling a version downgrade. Otherwise a * downgrade-capable context (e.g. from a v23 method) could silently fall * back to (D)TLS 1.2 and complete without any PSK. */ ctx->method->downgrade = 0; #endif return 0; } /* Require that an external Pre-Shared Key is negotiated for the handshake to * succeed. See wolfSSL_CTX_require_psk(). * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL or not at least TLS v1.3, 0 on success. */ int wolfSSL_require_psk(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ssl->options.failNoPSK = 1; /* See wolfSSL_CTX_require_psk() - keep the requirement fail-closed by * disabling a version downgrade to (D)TLS 1.2. */ ssl->options.downgrade = 0; #endif return 0; } int Tls13UpdateKeys(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #ifdef WOLFSSL_QUIC /* RFC 9001 Section 6: a QUIC connection must not send a TLS KeyUpdate; * key updates are handled at the QUIC packet-protection layer. */ if (WOLFSSL_IS_QUIC(ssl)) return BAD_FUNC_ARG; #endif #ifdef WOLFSSL_DTLS13 /* we are already waiting for the ack of a sent key update message. We can't send another one before receiving its ack. Either wolfSSL_update_keys() was invoked multiple times over a short period of time or we replied to a KeyUpdate with update request. We'll just ignore sending this KeyUpdate. */ /* TODO: add WOLFSSL_ERROR_ALREADY_IN_PROGRESS type of error here */ if (ssl->options.dtls && ssl->dtls13WaitKeyUpdateAck) return 0; #endif /* WOLFSSL_DTLS13 */ return SendTls13KeyUpdate(ssl); } /* Update the keys for encryption and decryption. * If using non-blocking I/O and WOLFSSL_ERROR_WANT_WRITE is returned then * calling wolfSSL_write() will have the message sent when ready. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL, not using TLS v1.3, or running over * QUIC (RFC 9001 handles key updates at the QUIC packet-protection layer), * WOLFSSL_ERROR_WANT_WRITE when non-blocking I/O is not ready to write, * WOLFSSL_SUCCESS on success and otherwise failure. */ int wolfSSL_update_keys(WOLFSSL* ssl) { int ret; ret = Tls13UpdateKeys(ssl); if (ret == WC_NO_ERR_TRACE(WANT_WRITE)) ret = WOLFSSL_ERROR_WANT_WRITE; else if (ret == 0) ret = WOLFSSL_SUCCESS; return ret; } /* Whether a response is waiting for key update request. * * ssl The SSL/TLS object. * required 0 when no key update response required. * 1 when no key update response required. * return 0 on success. * return BAD_FUNC_ARG when ssl is NULL or not using TLS v1.3 */ int wolfSSL_key_update_response(WOLFSSL* ssl, int* required) { if (required == NULL || ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; *required = ssl->keys.updateResponseReq; return 0; } #if !defined(NO_CERTS) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) /* Allow post-handshake authentication in TLS v1.3 connections. * * ctx The SSL/TLS CTX object. * returns BAD_FUNC_ARG when ctx is NULL, SIDE_ERROR when not a client and * 0 on success. */ int wolfSSL_CTX_allow_post_handshake_auth(WOLFSSL_CTX* ctx) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; if (ctx->method->side == WOLFSSL_SERVER_END) return SIDE_ERROR; ctx->postHandshakeAuth = 1; return 0; } /* Allow post-handshake authentication in TLS v1.3 connection. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL, not using TLS v1.3, or running over * QUIC, SIDE_ERROR when not a client, BAD_STATE_E when called after the * handshake has started, and 0 on success. * * Must be called before wolfSSL_connect() so the post_handshake_auth * extension can be included in the ClientHello. */ int wolfSSL_allow_post_handshake_auth(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #ifdef WOLFSSL_QUIC if (WOLFSSL_IS_QUIC(ssl)) return BAD_FUNC_ARG; #endif if (ssl->options.side == WOLFSSL_SERVER_END) return SIDE_ERROR; if (ssl->options.handShakeState != NULL_STATE) return BAD_STATE_E; ssl->options.postHandshakeAuth = 1; return 0; } /* Request a certificate of the client. * Can be called any time after handshake completion. * A maximum of 256 requests can be sent on a connection. * * ssl SSL/TLS object. */ int wolfSSL_request_certificate(WOLFSSL* ssl) { int ret; #ifndef NO_WOLFSSL_SERVER CertReqCtx* certReqCtx; #endif if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #ifdef WOLFSSL_QUIC if (WOLFSSL_IS_QUIC(ssl)) return BAD_FUNC_ARG; #endif #ifndef NO_WOLFSSL_SERVER if (ssl->options.side == WOLFSSL_CLIENT_END) return SIDE_ERROR; if (ssl->options.handShakeState != HANDSHAKE_DONE) return NOT_READY_ERROR; if (!ssl->options.postHandshakeAuth) return POST_HAND_AUTH_ERROR; if (ssl->certReqCtx != NULL) { if (ssl->certReqCtx->len != 1) return BAD_STATE_E; /* We support sending up to 255 certificate requests */ if (ssl->certReqCtx->ctx == 255) return BAD_STATE_E; } certReqCtx = (CertReqCtx*)XMALLOC(sizeof(CertReqCtx), ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (certReqCtx == NULL) return MEMORY_E; XMEMSET(certReqCtx, 0, sizeof(CertReqCtx)); certReqCtx->next = ssl->certReqCtx; certReqCtx->len = 1; if (certReqCtx->next != NULL) certReqCtx->ctx = certReqCtx->next->ctx + 1; ssl->certReqCtx = certReqCtx; ssl->msgsReceived.got_certificate = 0; ssl->msgsReceived.got_certificate_verify = 0; ssl->msgsReceived.got_finished = 0; /* Each round must prove possession again; these are only ever set to 1. */ ssl->options.havePeerCert = 0; ssl->options.havePeerVerify = 0; ret = SendTls13CertificateRequest(ssl, &certReqCtx->ctx, certReqCtx->len); /* Nothing follows a post-handshake request, so flush it even if grouped. */ if (ret == 0 && ssl->options.groupMessages) ret = SendBuffered(ssl); if (ret == WC_NO_ERR_TRACE(WANT_WRITE)) ret = WOLFSSL_ERROR_WANT_WRITE; else if (ret == 0) ret = WOLFSSL_SUCCESS; #else ret = SIDE_ERROR; #endif return ret; } #endif /* !NO_CERTS && WOLFSSL_POST_HANDSHAKE_AUTH */ #if !defined(WOLFSSL_NO_SERVER_GROUPS_EXT) /* Get the preferred key exchange group. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL or not using TLS v1.3, * SIDE_ERROR when not a client, NOT_READY_ERROR when handshake not complete * and group number on success. */ int wolfSSL_preferred_group(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; #ifndef NO_WOLFSSL_CLIENT if (ssl->options.side == WOLFSSL_SERVER_END) return SIDE_ERROR; if (ssl->options.handShakeState != HANDSHAKE_DONE) return NOT_READY_ERROR; #ifdef HAVE_SUPPORTED_CURVES /* Return supported groups only. */ return TLSX_SupportedCurve_Preferred(ssl, 1); #else return 0; #endif #else return SIDE_ERROR; #endif } #endif #ifndef NO_PSK /* Set the PSK callback, that is passed the cipher suite, for a client to use * against context object. * * @param [in, out] ctx SSL/TLS context object. * @param [in] cb Client PSK callback passed a cipher suite. */ void wolfSSL_CTX_set_psk_client_cs_callback(WOLFSSL_CTX* ctx, wc_psk_client_cs_callback cb) { WOLFSSL_ENTER("wolfSSL_CTX_set_psk_client_cs_callback"); if (ctx == NULL) return; ctx->havePSK = 1; ctx->client_psk_cs_cb = cb; } /* Set the PSK callback, that is passed the cipher suite, for a client to use * against SSL object. * * @param [in, out] ssl SSL/TLS object. * @param [in] cb Client PSK callback passed a cipher suite. */ void wolfSSL_set_psk_client_cs_callback(WOLFSSL* ssl, wc_psk_client_cs_callback cb) { byte haveRSA = 1; int keySz = 0; WOLFSSL_ENTER("wolfSSL_set_psk_client_cs_callback"); if (ssl == NULL) return; ssl->options.havePSK = 1; ssl->options.client_psk_cs_cb = cb; #ifdef NO_RSA haveRSA = 0; #endif #ifndef NO_CERTS keySz = ssl->buffers.keySz; #endif if (AllocateSuites(ssl) != 0) return; InitSuites(ssl->suites, ssl->version, keySz, haveRSA, TRUE, ssl->options.haveDH, ssl->options.haveECDSAsig, ssl->options.haveECC, TRUE, ssl->options.haveStaticECC, ssl->options.useAnon, TRUE, TRUE, TRUE, TRUE, ssl->options.side); } /* Set the PSK callback that returns the cipher suite for a client to use * against context object. * * @param [in, out] ctx SSL/TLS context object. * @param [in] cb Client PSK callback returning cipher suite. */ void wolfSSL_CTX_set_psk_client_tls13_callback(WOLFSSL_CTX* ctx, wc_psk_client_tls13_callback cb) { WOLFSSL_ENTER("wolfSSL_CTX_set_psk_client_tls13_callback"); if (ctx == NULL) return; ctx->havePSK = 1; ctx->client_psk_tls13_cb = cb; } /* Set the PSK callback that returns the cipher suite for a client to use * against SSL object. * * @param [in, out] ssl SSL/TLS object. * @param [in] cb Client PSK callback returning cipher suite. */ void wolfSSL_set_psk_client_tls13_callback(WOLFSSL* ssl, wc_psk_client_tls13_callback cb) { byte haveRSA = 1; int keySz = 0; WOLFSSL_ENTER("wolfSSL_set_psk_client_tls13_callback"); if (ssl == NULL) return; ssl->options.havePSK = 1; ssl->options.client_psk_tls13_cb = cb; #ifdef NO_RSA haveRSA = 0; #endif #ifndef NO_CERTS keySz = ssl->buffers.keySz; #endif if (AllocateSuites(ssl) != 0) return; InitSuites(ssl->suites, ssl->version, keySz, haveRSA, TRUE, ssl->options.haveDH, ssl->options.haveECDSAsig, ssl->options.haveECC, TRUE, ssl->options.haveStaticECC, ssl->options.useAnon, TRUE, TRUE, TRUE, TRUE, ssl->options.side); } /* Set the PSK callback that returns the cipher suite for a server to use * against context object. * * @param [in, out] ctx SSL/TLS context object. * @param [in] cb Server PSK callback returning cipher suite. */ void wolfSSL_CTX_set_psk_server_tls13_callback(WOLFSSL_CTX* ctx, wc_psk_server_tls13_callback cb) { WOLFSSL_ENTER("wolfSSL_CTX_set_psk_server_tls13_callback"); if (ctx == NULL) return; ctx->havePSK = 1; ctx->server_psk_tls13_cb = cb; } /* Set the PSK callback that returns the cipher suite for a server to use * against SSL object. * * @param [in, out] ssl SSL/TLS object. * @param [in] cb Server PSK callback returning cipher suite. */ void wolfSSL_set_psk_server_tls13_callback(WOLFSSL* ssl, wc_psk_server_tls13_callback cb) { byte haveRSA = 1; int keySz = 0; WOLFSSL_ENTER("wolfSSL_set_psk_server_tls13_callback"); if (ssl == NULL) return; ssl->options.havePSK = 1; ssl->options.server_psk_tls13_cb = cb; #ifdef NO_RSA haveRSA = 0; #endif #ifndef NO_CERTS keySz = ssl->buffers.keySz; #endif if (AllocateSuites(ssl) != 0) return; InitSuites(ssl->suites, ssl->version, keySz, haveRSA, TRUE, ssl->options.haveDH, ssl->options.haveECDSAsig, ssl->options.haveECC, TRUE, ssl->options.haveStaticECC, ssl->options.useAnon, TRUE, TRUE, TRUE, TRUE, ssl->options.side); } /* Get name of first supported cipher suite that uses the hash indicated. * * @param [in] ssl SSL/TLS object. * @param [in] hash Name of hash algorithm. e.g. "SHA256", "SHA384" * @return Name of cipher suite. * @return NULL on failure. */ const char* wolfSSL_get_cipher_name_by_hash(WOLFSSL* ssl, const char* hash) { const char* name = NULL; byte mac = no_mac; int i; const Suites* suites; if (hash == NULL || ssl == NULL || (ssl->suites == NULL && ssl->ctx == NULL)) return NULL; suites = WOLFSSL_SUITES(ssl); if (suites == NULL) return NULL; if (XSTRCMP(hash, "SHA256") == 0) { mac = sha256_mac; } else if (XSTRCMP(hash, "SHA384") == 0) { mac = sha384_mac; } if (mac != no_mac) { for (i = 0; i < suites->suiteSz; i += 2) { if (SuiteMac(suites->suites + i) == mac) { name = GetCipherNameInternal(suites->suites[i + 0], suites->suites[i + 1]); break; } } } return name; } #endif /* !NO_PSK */ #ifndef NO_WOLFSSL_SERVER /* Send the RFC 8446 Appendix D.4 ChangeCipherSpec a server owes a client that * offered a non-empty legacy_session_id. * * ssl The SSL/TLS object. * flush Force the record out on its own. A HelloRetryRequest needs this * because SendTls13ServerHello has already sent it, unlike a * ServerHello, which waits for the rest of its flight. * returns 0 on success. */ static int SendTls13ServerChangeCipher(WOLFSSL* ssl, int flush) { int ret; /* DoTls13ClientHello clears tls13MiddleBoxCompat for a QUIC peer, so the * check here is a local backstop, 0 when QUIC is not built. */ if (ssl->options.dtls || WOLFSSL_IS_QUIC(ssl) || !ssl->options.tls13MiddleBoxCompat || ssl->options.sentChangeCipher) { return 0; } ret = SendChangeCipher(ssl); /* A short send leaves the record queued in the output buffer. Mark it sent * anyway, or the resumed accept, which comes back in at the ServerHello * case, puts a second record on the wire. */ if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) ssl->options.sentChangeCipher = 1; if (ret == 0 && flush && ssl->options.groupMessages) ret = SendBuffered(ssl); return ret; } /* The server accepting a connection from a client. * The protocol version is expecting to be TLS v1.3. * If the client downgrades, and older versions of the protocol are compiled * in, the server will fallback to wolfSSL_accept(). * Please see note at top of README if you get an error from accept. * * ssl The SSL/TLS object. * returns WOLFSSL_SUCCESS on successful handshake, WOLFSSL_FATAL_ERROR when * unrecoverable error occurs and 0 otherwise. * For more error information use wolfSSL_get_error(). */ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) { #if !defined(NO_CERTS) && (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) word16 havePSK = 0; #endif int ret = 0; WOLFSSL_ENTER("wolfSSL_accept_TLSv13"); #ifdef HAVE_ERRNO_H errno = 0; #endif if (ssl == NULL) return WOLFSSL_FATAL_ERROR; #if !defined(NO_CERTS) && (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) havePSK = ssl->options.havePSK; #endif if (ssl->options.side != WOLFSSL_SERVER_END) { ssl->error = SIDE_ERROR; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } /* make sure this wolfSSL object has arrays and rng setup. Protects * case where the WOLFSSL object is reused via wolfSSL_clear() */ if ((ret = ReinitSSL(ssl, ssl->ctx, 0)) != 0) { return ret; } #ifdef WOLFSSL_DTLS if (ssl->version.major == DTLS_MAJOR) { ssl->options.dtls = 1; if (!IsDtlsNotSctpMode(ssl) || !ssl->options.sendCookie) ssl->options.dtlsStateful = 1; } #endif #ifdef WOLFSSL_WOLFSENTRY_HOOKS if ((ssl->AcceptFilter != NULL) && ((ssl->options.acceptState == TLS13_ACCEPT_BEGIN) #ifdef HAVE_SECURE_RENEGOTIATION || (ssl->options.acceptState == TLS13_ACCEPT_BEGIN_RENEG) #endif )) { wolfSSL_netfilter_decision_t res; if ((ssl->AcceptFilter(ssl, ssl->AcceptFilter_arg, &res) == WOLFSSL_SUCCESS) && (res == WOLFSSL_NETFILTER_REJECT)) { ssl->error = SOCKET_FILTERED_E; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_WOLFSENTRY_HOOKS */ #ifndef NO_CERTS #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) if (!havePSK) #endif { #if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \ defined(WOLFSSL_NGINX) || defined (WOLFSSL_HAPROXY) if (ssl->ctx->certSetupCb != NULL) { WOLFSSL_MSG("CertSetupCb set. server cert and " "key not checked"); } else #endif { if (!ssl->buffers.certificate || !ssl->buffers.certificate->buffer) { WOLFSSL_MSG("accept error: server cert required"); ssl->error = NO_PRIVATE_KEY; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } if (!ssl->buffers.key || !ssl->buffers.key->buffer) { /* allow no private key if using existing key */ #ifdef WOLF_PRIVATE_KEY_ID if (ssl->devId != INVALID_DEVID #ifdef HAVE_PK_CALLBACKS || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx) #endif ) { WOLFSSL_MSG("Allowing no server private key (external)"); } else #endif { WOLFSSL_MSG("accept error: server key required"); ssl->error = NO_PRIVATE_KEY; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } } } #endif /* NO_CERTS */ if (ssl->buffers.outputBuffer.length > 0 #ifdef WOLFSSL_HAVE_HS_SUSPEND /* do not send buffered or advance state if the last error suspended the handshake - advancing frees the saved state */ && !IsHsSuspendErr(ssl->error) #endif ) { /* fragOffset is non-zero when sending fragments. On the last * fragment, fragOffset is zero again, and the state can be * advanced. */ int advanceState = (ssl->options.acceptState == TLS13_ACCEPT_CLIENT_HELLO_DONE || ssl->options.acceptState == TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE || ssl->options.acceptState == TLS13_ACCEPT_SECOND_REPLY_DONE || ssl->options.acceptState == TLS13_SERVER_HELLO_SENT || ssl->options.acceptState == TLS13_ACCEPT_THIRD_REPLY_DONE || ssl->options.acceptState == TLS13_SERVER_EXTENSIONS_SENT || ssl->options.acceptState == TLS13_CERT_REQ_SENT || ssl->options.acceptState == TLS13_CERT_SENT || ssl->options.acceptState == TLS13_CERT_VERIFY_SENT || ssl->options.acceptState == TLS13_ACCEPT_FINISHED_SENT || ssl->options.acceptState == TLS13_ACCEPT_FINISHED_DONE); #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) advanceState = advanceState && !ssl->dtls13SendingFragments && !ssl->dtls13SendingAckOrRtx; #endif /* WOLFSSL_DTLS13 */ ret = SendBuffered(ssl); if (ret == 0) { if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) { if (advanceState) { ssl->options.acceptState++; WOLFSSL_MSG("accept state: " "Advanced from last buffered fragment send"); #ifdef WOLFSSL_ASYNC_IO FreeAsyncCtx(ssl, 0); #endif } } else { WOLFSSL_MSG("accept state: " "Not advanced, more fragments to send"); } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) ssl->dtls13SendingAckOrRtx = 0; #endif /* WOLFSSL_DTLS13 */ } else { ssl->error = ret; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ret = RetrySendAlert(ssl); if (ret != 0) { ssl->error = ret; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && ssl->dtls13SendingFragments) { if ((ssl->error = Dtls13FragmentsContinue(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } /* we sent all the fragments. Advance state. */ ssl->options.acceptState++; } #endif /* WOLFSSL_DTLS13 */ switch (ssl->options.acceptState) { #ifdef HAVE_SECURE_RENEGOTIATION case TLS13_ACCEPT_BEGIN_RENEG: #endif case TLS13_ACCEPT_BEGIN : /* get client_hello */ while (ssl->options.clientState < CLIENT_HELLO_COMPLETE) { if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_DTLS13 */ } #ifdef WOLFSSL_DTLS13 /* Notify once at the first CH transition, not while reassembling * or processing the second CH after a key-share HRR. */ if (ssl->options.acceptState == TLS13_ACCEPT_BEGIN) { if ((ssl->error = DtlsNoCookieChGood(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif ssl->options.acceptState = TLS13_ACCEPT_CLIENT_HELLO_DONE; WOLFSSL_MSG("accept state ACCEPT_CLIENT_HELLO_DONE"); if (!IsAtLeastTLSv1_3(ssl->version)) return wolfSSL_accept(ssl); FALL_THROUGH; case TLS13_ACCEPT_CLIENT_HELLO_DONE : if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) { if ((ssl->error = SendTls13ServerHello(ssl, hello_retry_request)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ssl->options.acceptState = TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE; WOLFSSL_MSG("accept state ACCEPT_HELLO_RETRY_REQUEST_DONE"); FALL_THROUGH; case TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE : if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) { ssl->error = SendTls13ServerChangeCipher(ssl, 1); if (ssl->error != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ssl->options.acceptState = TLS13_ACCEPT_FIRST_REPLY_DONE; WOLFSSL_MSG("accept state ACCEPT_FIRST_REPLY_DONE"); FALL_THROUGH; case TLS13_ACCEPT_FIRST_REPLY_DONE : if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE) { ssl->options.clientState = CLIENT_HELLO_RETRY; while (ssl->options.clientState < CLIENT_HELLO_COMPLETE) { if ((ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_DTLS13 */ } } ssl->options.acceptState = TLS13_ACCEPT_SECOND_REPLY_DONE; WOLFSSL_MSG("accept state ACCEPT_SECOND_REPLY_DONE"); FALL_THROUGH; case TLS13_ACCEPT_SECOND_REPLY_DONE : if (ssl->options.returnOnGoodCh) { /* Higher level in stack wants us to return. Simulate a * WANT_WRITE to accomplish this. */ ssl->error = WANT_WRITE; return WOLFSSL_FATAL_ERROR; } if ((ssl->error = SendTls13ServerHello(ssl, server_hello)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } ssl->options.acceptState = TLS13_SERVER_HELLO_SENT; WOLFSSL_MSG("accept state SERVER_HELLO_SENT"); FALL_THROUGH; case TLS13_SERVER_HELLO_SENT : ssl->error = SendTls13ServerChangeCipher(ssl, 0); if (ssl->error != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } ssl->options.acceptState = TLS13_ACCEPT_THIRD_REPLY_DONE; WOLFSSL_MSG("accept state ACCEPT_THIRD_REPLY_DONE"); FALL_THROUGH; case TLS13_ACCEPT_THIRD_REPLY_DONE : #ifdef HAVE_SUPPORTED_CURVES #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) if (!ssl->options.noPskDheKe) #endif { ssl->error = TLSX_KeyShare_DeriveSecret(ssl); if (ssl->error != 0) return WOLFSSL_FATAL_ERROR; } #endif if ((ssl->error = SendTls13EncryptedExtensions(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } ssl->options.acceptState = TLS13_SERVER_EXTENSIONS_SENT; WOLFSSL_MSG("accept state SERVER_EXTENSIONS_SENT"); FALL_THROUGH; case TLS13_SERVER_EXTENSIONS_SENT : #ifndef NO_CERTS if (!ssl->options.resuming) { if (ssl->options.verifyPeer #ifdef WOLFSSL_POST_HANDSHAKE_AUTH && !ssl->options.verifyPostHandshake #endif ) { ssl->error = SendTls13CertificateRequest(ssl, NULL, 0); if (ssl->error != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } else { /* SERVER: Peer auth good if not verifying client. */ ssl->options.peerAuthGood = 1; } } #endif ssl->options.acceptState = TLS13_CERT_REQ_SENT; WOLFSSL_MSG("accept state CERT_REQ_SENT"); FALL_THROUGH; case TLS13_CERT_REQ_SENT : #ifndef NO_CERTS if (!ssl->options.resuming && ssl->options.sendVerify) { if ((ssl->error = SendTls13Certificate(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif ssl->options.acceptState = TLS13_CERT_SENT; WOLFSSL_MSG("accept state CERT_SENT"); FALL_THROUGH; case TLS13_CERT_SENT : #if !defined(NO_CERTS) && (!defined(NO_RSA) || defined(HAVE_ECC) || \ defined(HAVE_ED25519) || defined(HAVE_ED448) || defined(HAVE_FALCON) || \ defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA)) if (!ssl->options.resuming && ssl->options.sendVerify) { if ((ssl->error = SendTls13CertificateVerify(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif ssl->options.acceptState = TLS13_CERT_VERIFY_SENT; WOLFSSL_MSG("accept state CERT_VERIFY_SENT"); FALL_THROUGH; case TLS13_CERT_VERIFY_SENT : if ((ssl->error = SendTls13Finished(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } ssl->options.acceptState = TLS13_ACCEPT_FINISHED_SENT; WOLFSSL_MSG("accept state ACCEPT_FINISHED_SENT"); FALL_THROUGH; case TLS13_ACCEPT_FINISHED_SENT: #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data && ssl->options.handShakeState != SERVER_FINISHED_COMPLETE) { ssl->options.handShakeState = SERVER_FINISHED_COMPLETE; return WOLFSSL_SUCCESS; } #endif #ifdef HAVE_SESSION_TICKET #ifdef WOLFSSL_TLS13_TICKET_BEFORE_FINISHED if (!ssl->options.verifyPeer && !ssl->options.noTicketTls13 && ssl->ctx->ticketEncCb != NULL && ssl->options.maxTicketTls13 > 0) { if ((ssl->error = SendTls13NewSessionTicket(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } ssl->options.ticketsSent = 1; } #endif #endif /* HAVE_SESSION_TICKET */ ssl->options.acceptState = TLS13_PRE_TICKET_SENT; WOLFSSL_MSG("accept state TICKET_SENT"); FALL_THROUGH; case TLS13_PRE_TICKET_SENT : while (ssl->options.clientState < CLIENT_FINISHED_COMPLETE) { if ( (ssl->error = ProcessReply(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_DTLS13 */ } /* Finish a key schedule the Finished handler left pending: * it must complete before this side's sends advance the * transcript the traffic secrets hash. */ if (ssl->kdfMsgStep > 0) { ssl->error = DoTls13MsgDerives(ssl, ssl->kdfMsgType); if (ssl->error != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ssl->options.acceptState = TLS13_ACCEPT_FINISHED_DONE; WOLFSSL_MSG("accept state ACCEPT_FINISHED_DONE"); FALL_THROUGH; 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. */ if (!ssl->options.resuming && ssl->options.verifyPeer && #ifdef WOLFSSL_POST_HANDSHAKE_AUTH !ssl->options.verifyPostHandshake && #endif !ssl->options.havePeerCert && !ssl->options.failNoCert) { ssl->options.peerAuthGood = 1; } /* SERVER: check peer authentication. */ if (!ssl->options.peerAuthGood) { WOLFSSL_MSG("Client authentication did not happen"); return WOLFSSL_FATAL_ERROR; } #ifdef HAVE_SESSION_TICKET while (ssl->options.ticketsSent < ssl->options.maxTicketTls13) { if (!ssl->options.noTicketTls13 && ssl->ctx->ticketEncCb != NULL) { if ((ssl->error = SendTls13NewSessionTicket(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } ssl->options.ticketsSent++; /* only one session ticket is sent on session resumption */ if (ssl->options.resuming) { break; } } #endif /* HAVE_SESSION_TICKET */ ssl->options.acceptState = TLS13_TICKET_SENT; WOLFSSL_MSG("accept state TICKET_SENT"); FALL_THROUGH; case TLS13_TICKET_SENT : #ifndef NO_HANDSHAKE_DONE_CB if (ssl->hsDoneCb) { int cbret = ssl->hsDoneCb(ssl, ssl->hsDoneCtx); if (cbret < 0) { ssl->error = cbret; WOLFSSL_MSG("HandShake Done Cb don't continue error"); return WOLFSSL_FATAL_ERROR; } } #endif /* NO_HANDSHAKE_DONE_CB */ if (!ssl->options.keepResources) { FreeHandshakeResources(ssl); } #ifdef WOLFSSL_HAVE_HS_SUSPEND /* A post-handshake Certificate may be suspended in ssl->async. * Keep it and the error so wolfSSL_read() can resume it. */ if (!IsHsSuspendErr(ssl->error)) #endif { #if defined(WOLFSSL_ASYNC_IO) && !defined(WOLFSSL_ASYNC_CRYPT) /* Free the remaining async context if not using it for * crypto */ FreeAsyncCtx(ssl, 1); #endif ssl->error = 0; /* clear the error */ } WOLFSSL_LEAVE("wolfSSL_accept", WOLFSSL_SUCCESS); return WOLFSSL_SUCCESS; default: WOLFSSL_MSG("Unknown accept state ERROR"); return WOLFSSL_FATAL_ERROR; } } #endif #if !defined(NO_WOLFSSL_SERVER) && defined(HAVE_SESSION_TICKET) /* Server sends a session ticket to the peer. * * RFC 8446, section 4.6.1, para 1. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL, or not using TLS v1.3, * SIDE_ERROR when not a server, * NOT_READY_ERROR when handshake not complete, * MISSING_HANDSHAKE_DATA when the ClientHello had no * psk_key_exchange_modes extension and * WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES is defined, * PSK_KEY_ERROR when no advertised PSK key exchange mode is usable and * WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES is defined, * WOLFSSL_FATAL_ERROR when creating or sending message fails, and * WOLFSSL_SUCCESS on success. */ int wolfSSL_send_SessionTicket(WOLFSSL* ssl) { int ret; if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; if (ssl->options.side == WOLFSSL_CLIENT_END) return SIDE_ERROR; if (ssl->options.handShakeState != HANDSHAKE_DONE) return NOT_READY_ERROR; ret = CheckTls13TicketPskModes(ssl); if (ret != 0) { WOLFSSL_ERROR_VERBOSE(ret); return ret; } if ((ssl->error = SendTls13NewSessionTicket(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } ssl->options.ticketsSent++; return WOLFSSL_SUCCESS; } #endif #ifdef WOLFSSL_EARLY_DATA /* Sets the maximum amount of early data that can be seen by server when using * session tickets for resumption. * A value of zero indicates no early data is to be sent by client using session * tickets. * * The default value is zero: per RFC 8446 Appendix E.5, TLS implementations * "MUST NOT enable 0-RTT (either sending or accepting) unless specifically * requested by the application." Servers must explicitly opt in by calling * this function (or the per-SSL equivalent) with a non-zero value. * * ctx The SSL/TLS CTX object. * sz Maximum size of the early data. * returns BAD_FUNC_ARG when ctx is NULL, SIDE_ERROR when not a server and * 0 on success. */ int wolfSSL_CTX_set_max_early_data(WOLFSSL_CTX* ctx, unsigned int sz) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; if (ctx->method->side == WOLFSSL_CLIENT_END) return SIDE_ERROR; ctx->maxEarlyDataSz = sz; #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_ERROR_CODE_OPENSSL) /* 1 on success in OpenSSL*/ return WOLFSSL_SUCCESS; #else return 0; #endif } /* Disable the RFC 8446 Section 8.2 fresh start protection. Early data is * then accepted for tickets minted before this ctx was created. Only use * this when the anti-replay state reliably survives server restarts. * * The check needs TimeNowInMilliseconds() to be comparable across restarts. * On ports where it counts from boot the check never fires for tickets * minted before a reboot. * * ctx The SSL/TLS CTX object. * returns BAD_FUNC_ARG when ctx is NULL or not TLS v1.3, SIDE_ERROR when * called with a client and 0 on success. */ int wolfSSL_CTX_no_early_data_fresh_start_check(WOLFSSL_CTX* ctx) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; if (ctx->method->side == WOLFSSL_CLIENT_END) return SIDE_ERROR; #ifdef HAVE_SESSION_TICKET ctx->noFreshStartCheck = 1; #endif return 0; } /* Sets the maximum amount of early data that a client or server would like * to exchange. Servers will advertise this value in session tickets sent * to a client. * A value of zero indicates no early data will be sent by a client, or * no early data is accepted by a server (and announced as such in send out * session tickets). * * ssl The SSL/TLS object. * sz Maximum size of the early data. * returns BAD_FUNC_ARG when ssl is NULL, or not using TLS v1.3, * and 0 on success. */ int wolfSSL_set_max_early_data(WOLFSSL* ssl, unsigned int sz) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; ssl->options.maxEarlyDataSz = sz; #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_ERROR_CODE_OPENSSL) /* 1 on success in OpenSSL*/ return WOLFSSL_SUCCESS; #else return 0; #endif } /* Gets the maximum amount of early data that can be seen by server when using * session tickets for resumption. * A value of zero indicates no early data is to be sent by client using session * tickets. * * ctx The SSL/TLS CTX object. * returns BAD_FUNC_ARG when ctx is NULL, SIDE_ERROR when not a server and * returns the maximum amount of early data to be set */ int wolfSSL_CTX_get_max_early_data(WOLFSSL_CTX* ctx) { if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version)) return BAD_FUNC_ARG; if (ctx->method->side == WOLFSSL_CLIENT_END) return SIDE_ERROR; return ctx->maxEarlyDataSz; } /* Gets the maximum amount of early data that can be seen by server when using * session tickets for resumption. * A value of zero indicates no early data is to be sent by client using session * tickets. * * ssl The SSL/TLS object. * returns BAD_FUNC_ARG when ssl is NULL, or not using TLS v1.3, * SIDE_ERROR when not a server and * returns the maximum amount of early data to be set */ int wolfSSL_get_max_early_data(WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; return ssl->options.maxEarlyDataSz; } /* Write early data to the server. * * ssl The SSL/TLS object. * data Early data to write * sz The size of the early data in bytes. * outSz The number of early data bytes written. * returns BAD_FUNC_ARG when: ssl, data or outSz is NULL; sz is negative; * or not using TLS v1.3. SIDE ERROR when not a server. BAD_STATE_E if invoked * without a valid session or without a valid PSK CB. * Otherwise the number of early data bytes written. */ int wolfSSL_write_early_data(WOLFSSL* ssl, const void* data, int sz, int* outSz) { int ret = 0; WOLFSSL_ENTER("wolfSSL_write_early_data"); if (ssl == NULL || data == NULL || sz < 0 || outSz == NULL) return BAD_FUNC_ARG; if (!IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; *outSz = 0; #ifndef NO_WOLFSSL_CLIENT if (ssl->options.side == WOLFSSL_SERVER_END) return SIDE_ERROR; /* Early data requires PSK or session resumption */ if (!EarlyDataPossible(ssl)) { return BAD_STATE_E; } if (ssl->options.handShakeState == NULL_STATE) { /* avoid re-setting ssl->earlyData if we re-enter the function because * of WC_PENDING_E, WANT_WRITE or WANT_READ */ if (ssl->error == 0) ssl->earlyData = expecting_early_data; ret = wolfSSL_connect_TLSv13(ssl); if (ret != WOLFSSL_SUCCESS) return WOLFSSL_FATAL_ERROR; /* on client side, status is set to rejected */ /* until sever accepts the early data extension. */ ssl->earlyDataStatus = WOLFSSL_EARLY_DATA_REJECTED; } if (ssl->options.handShakeState == CLIENT_HELLO_COMPLETE) { #ifdef OPENSSL_EXTRA /* when processed early data exceeds max size */ if (ssl->session->maxEarlyDataSz > 0 && (ssl->earlyDataSz + sz > ssl->session->maxEarlyDataSz)) { ssl->error = TOO_MUCH_EARLY_DATA; return WOLFSSL_FATAL_ERROR; } #endif ret = SendData(ssl, data, sz); if (ret > 0) { *outSz = ret; /* store amount of processed early data from client */ ssl->earlyDataSz += ret; } } #else return SIDE_ERROR; #endif WOLFSSL_LEAVE("wolfSSL_write_early_data", ret); if (ret < 0) ret = WOLFSSL_FATAL_ERROR; return ret; } /* Read the any early data from the client. * * ssl The SSL/TLS object. * data Buffer to put the early data into. * sz The size of the buffer in bytes. * outSz The number of early data bytes read. * returns BAD_FUNC_ARG when: ssl, data or outSz is NULL; sz is negative; * or not using TLS v1.3. SIDE ERROR when not a server. Otherwise the number of * early data bytes read. */ int wolfSSL_read_early_data(WOLFSSL* ssl, void* data, int sz, int* outSz) { int ret = 0; WOLFSSL_ENTER("wolfSSL_read_early_data"); if (ssl == NULL || data == NULL || sz < 0 || outSz == NULL) return BAD_FUNC_ARG; if (!IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; *outSz = 0; #ifndef NO_WOLFSSL_SERVER if (ssl->options.side == WOLFSSL_CLIENT_END) return SIDE_ERROR; if (ssl->options.handShakeState == NULL_STATE) { /* the server flight can return WANT_WRITE and we re-enter here after * setting ssl->earlyData = process_early_data, set earlyData to * expecting_early_data just once */ if (ssl->earlyData < expecting_early_data) ssl->earlyData = expecting_early_data; /* this used to be: ret = wolfSSL_accept_TLSv13(ssl); * However, wolfSSL_accept_TLSv13() expects a certificate to * be installed already, which is not the case in servers * such as HAProxy. They do it after inspecting the ClientHello. * The common wolfssl_accept() allows that. */ ret = wolfSSL_accept(ssl); if (ret <= 0) return WOLFSSL_FATAL_ERROR; } if (ssl->options.handShakeState == SERVER_FINISHED_COMPLETE) { ssl->options.clientInEarlyData = 1; ret = ReceiveData(ssl, (byte*)data, (size_t)sz, FALSE); ssl->options.clientInEarlyData = 0; if (ret > 0) *outSz = ret; /* Only a failed read means the handshake finished during the early * data read. ssl->error may still hold APP_DATA_READY from an earlier * wolfSSL_accept() while data was actually read. */ if ((ret < 0) && (ssl->error == WC_NO_ERR_TRACE(APP_DATA_READY))) { ret = 0; ssl->error = WOLFSSL_ERROR_NONE; #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { ret = Dtls13DoScheduledWork(ssl); if (ret < 0) { ssl->error = ret; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_DTLS13 */ } } #ifdef WOLFSSL_DTLS13 else if (ssl->buffers.outputBuffer.length > 0 && ssl->options.dtls && ssl->dtls13SendingAckOrRtx) { ret = SendBuffered(ssl); if (ret == 0) { ssl->dtls13SendingAckOrRtx = 0; } else { ssl->error = ret; WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; } } #endif /* WOLFSSL_DTLS13 */ else ret = 0; #else return SIDE_ERROR; #endif WOLFSSL_LEAVE("wolfSSL_read_early_data", ret); if (ret < 0) ret = WOLFSSL_FATAL_ERROR; return ret; } /* Returns early data status * * ssl The SSL/TLS object. * returns WOLFSSL_EARLY_DATA_ACCEPTED if the data was accepted * WOLFSSL_EARLY_DATA_REJECTED if the data was rejected * WOLFSSL_EARLY_DATA_NOT_SENT if no early data was sent */ int wolfSSL_get_early_data_status(const WOLFSSL* ssl) { if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) return BAD_FUNC_ARG; return ssl->earlyDataStatus; } #endif #ifdef HAVE_SECRET_CALLBACK int wolfSSL_set_tls13_secret_cb(WOLFSSL* ssl, Tls13SecretCb cb, void* ctx) { WOLFSSL_ENTER("wolfSSL_set_tls13_secret_cb"); if (ssl == NULL) return WOLFSSL_FATAL_ERROR; ssl->tls13SecretCb = cb; ssl->tls13SecretCtx = ctx; return WOLFSSL_SUCCESS; } #if defined(SHOW_SECRETS) && defined(WOLFSSL_SSLKEYLOGFILE) int tls13ShowSecrets(WOLFSSL* ssl, int id, const unsigned char* secret, int secretSz, void* ctx) { int i; const char* str = NULL; byte clientRandom[RAN_LEN]; int clientRandomSz; XFILE fp; #if defined(WOLFSSL_SSLKEYLOGFILE_OUTPUT) && defined(WOLFSSL_SSLKEYLOGFILE_USE_ENV) const char* keyLogFile; #endif (void) ctx; #ifdef WOLFSSL_SSLKEYLOGFILE_OUTPUT #ifdef WOLFSSL_SSLKEYLOGFILE_USE_ENV /* RFC 9850: prefer the SSLKEYLOGFILE environment variable so tools such as * curl and Wireshark can share the path, else use the compile-time path. * XGETENV resolves to NULL where environment access is unavailable. Opt-in * so a build with the variable exported for other applications is not * affected. */ keyLogFile = XGETENV("SSLKEYLOGFILE"); if (keyLogFile == NULL || keyLogFile[0] == '\0') keyLogFile = WOLFSSL_SSLKEYLOGFILE_OUTPUT; fp = XFOPEN(keyLogFile, "ab"); #else fp = XFOPEN(WOLFSSL_SSLKEYLOGFILE_OUTPUT, "ab"); #endif if (fp == XBADFILE) { return BAD_FUNC_ARG; } #else fp = stderr; #endif clientRandomSz = (int)wolfSSL_get_client_random(ssl, clientRandom, sizeof(clientRandom)); if (clientRandomSz <= 0) { printf("Error getting server random %d\n", clientRandomSz); return BAD_FUNC_ARG; } #if 0 printf("TLS Server Secret CB: Rand %d, Secret %d\n", serverRandomSz, secretSz); #endif switch (id) { case CLIENT_EARLY_TRAFFIC_SECRET: str = "CLIENT_EARLY_TRAFFIC_SECRET"; break; case EARLY_EXPORTER_SECRET: str = "EARLY_EXPORTER_SECRET"; break; case CLIENT_HANDSHAKE_TRAFFIC_SECRET: str = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; break; case SERVER_HANDSHAKE_TRAFFIC_SECRET: str = "SERVER_HANDSHAKE_TRAFFIC_SECRET"; break; case CLIENT_TRAFFIC_SECRET: str = "CLIENT_TRAFFIC_SECRET_0"; break; case SERVER_TRAFFIC_SECRET: str = "SERVER_TRAFFIC_SECRET_0"; break; case EXPORTER_SECRET: str = "EXPORTER_SECRET"; break; #ifdef HAVE_ECH case ECH_SECRET: str = "ECH_SECRET"; break; case ECH_CONFIG: str = "ECH_CONFIG"; break; #endif default: #ifdef WOLFSSL_SSLKEYLOGFILE_OUTPUT XFCLOSE(fp); #endif return BAD_FUNC_ARG; break; } fprintf(fp, "%s ", str); for (i = 0; i < (int)clientRandomSz; i++) { fprintf(fp, "%02x", clientRandom[i]); } fprintf(fp, " "); for (i = 0; i < secretSz; i++) { fprintf(fp, "%02x", secret[i]); } fprintf(fp, "\n"); #ifdef WOLFSSL_SSLKEYLOGFILE_OUTPUT XFCLOSE(fp); #endif return 0; } #endif #endif #undef ERROR_OUT #endif /* !WOLFCRYPT_ONLY */ #endif /* !NO_TLS && WOLFSSL_TLS13 */