From 9ed061cc96251a77e1affd0f23a1b60a14bbb5a8 Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Mon, 14 Mar 2022 12:03:49 +1000 Subject: [PATCH] TLS: add peer authentication failsafe for TLS 1.2 and below Tightened the TLS 1.3 failsafe checks too. --- configure.ac | 7 +++-- src/internal.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++--- src/keys.c | 5 ++++ src/ssl.c | 33 +++++++++++++++++++++ src/tls.c | 8 ++++-- src/tls13.c | 53 ++++++++++++++++++++++++++++------ 6 files changed, 167 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index 36a60da2e..b982fde67 100644 --- a/configure.ac +++ b/configure.ac @@ -4470,7 +4470,7 @@ then fi if test "$ENABLED_TLS13_EARLY_DATA" = "yes" then - if test "x$ENABLED_TLS13" = "xno" + if test "x$ENABLED_TLS13" = "xno" && test "X$ENABLED_ALL" = "xno" then AC_MSG_ERROR([cannot enable earlydata without enabling tls13.]) fi @@ -4478,7 +4478,10 @@ then then AC_MSG_ERROR([cannot enable earlydata without enabling session tickets and/or PSK.]) fi - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_EARLY_DATA" + if test "x$ENABLED_TLS13" = "xyes" + then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_EARLY_DATA" + fi fi if test "$ENABLED_TLSV12" = "no" && test "$ENABLED_TLS13" = "yes" && test "x$ENABLED_SESSION_TICKET" = "xno" diff --git a/src/internal.c b/src/internal.c index 8bf04ccf8..f8a9afb14 100644 --- a/src/internal.c +++ b/src/internal.c @@ -12766,7 +12766,23 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, goto exit_ppc; } + /* Certificate validated and stored. */ ssl->options.havePeerCert = 1; + #if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_RSA) + if (ssl->options.side == WOLFSSL_CLIENT_END && + ssl->specs.sig_algo == rsa_kea) { + /* CLIENT: No ServerKeyExchange message sent by server. */ + ssl->options.peerAuthGood = 1; + } + #endif + #if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_ECC) + if (ssl->options.side == WOLFSSL_CLIENT_END && + ssl->specs.static_ecdh) { + /* CLIENT: No ServerKeyExchange message sent by server. */ + ssl->options.peerAuthGood = 1; + } + #endif + if (!ssl->options.verifyNone && ssl->buffers.domainName.buffer) { #ifndef WOLFSSL_ALLOW_NO_CN_IN_SAN @@ -14274,6 +14290,8 @@ static int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (ssl->options.resuming) { WOLFSSL_MSG("Not resuming as thought"); ssl->options.resuming = 0; + /* CLIENT: No longer resuming, reset peer authentication state. */ + ssl->options.peerAuthGood = 0; } break; @@ -21915,9 +21933,10 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) ssl->suites->sigAlgo = ssl->buffers.keyType; #endif } - else + else { ssl->suites->sigAlgo = ssl->specs.sig_algo; - if (ssl->suites->sigAlgo == 0) { + } + if (ssl->suites->sigAlgo == anonymous_sa_algo) { /* PSK ciphersuite - get digest to use from cipher suite */ ssl->suites->hashAlgo = ssl->specs.mac_algorithm; return 0; @@ -23459,6 +23478,8 @@ exit_dpk: if (!ssl->options.tls) ret = DeriveKeys(ssl); #endif /* NO_OLD_TLS */ + /* SERVER: peer auth based on session secret. */ + ssl->options.peerAuthGood = (ret == 0); ssl->options.serverState = SERVER_HELLODONE_COMPLETE; return ret; @@ -24812,6 +24833,9 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, (void**)&ssl->peerEccDsaKey); ssl->peerEccDsaKeyPresent = 0; } + /* CLIENT: Data verified with cert's public key. */ + ssl->options.peerAuthGood = + ssl->options.havePeerCert && (ret == 0); break; } #endif /* HAVE_ECC */ @@ -24839,6 +24863,9 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, (void**)&ssl->peerEd25519Key); ssl->peerEd25519KeyPresent = 0; } + /* CLIENT: Data verified with cert's public key. */ + ssl->options.peerAuthGood = + ssl->options.havePeerCert && (ret == 0); break; } #endif /* HAVE_ED25519 */ @@ -24866,6 +24893,9 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, (void**)&ssl->peerEd448Key); ssl->peerEd448KeyPresent = 0; } + /* CLIENT: Data verified with cert's public key. */ + ssl->options.peerAuthGood = + ssl->options.havePeerCert && (ret == 0); break; } #endif /* HAVE_ED448 */ @@ -24937,6 +24967,9 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, #endif if (ret != 0) return ret; + /* CLIENT: Data verified with cert's public key. */ + ssl->options.peerAuthGood = + ssl->options.havePeerCert; break; #endif case rsa_sa_algo: @@ -24989,6 +25022,9 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, FINISHED_SZ) != 0) { ERROR_OUT(VERIFY_SIGN_ERROR, exit_dske); } + /* CLIENT: Data verified with cert's public key. */ + ssl->options.peerAuthGood = + ssl->options.havePeerCert; break; } #endif /* !NO_RSA */ @@ -25526,6 +25562,8 @@ int SendClientKeyExchange(WOLFSSL* ssl) } XMEMCPY(args->encSecret, ssl->arrays->client_identity, args->encSz); + /* CLIENT: Pre-shared Key for peer authentication. */ + ssl->options.peerAuthGood = 1; /* make psk pre master secret */ /* length of key + length 0s + length of key + key */ @@ -25562,6 +25600,8 @@ int SendClientKeyExchange(WOLFSSL* ssl) if (esSz > MAX_PSK_ID_LEN) { ERROR_OUT(CLIENT_ID_ERROR, exit_scke); } + /* CLIENT: Pre-shared Key for peer authentication. */ + ssl->options.peerAuthGood = 1; ssl->buffers.sig.length = ENCRYPT_LEN; ssl->buffers.sig.buffer = (byte*)XMALLOC(ENCRYPT_LEN, @@ -25640,6 +25680,8 @@ int SendClientKeyExchange(WOLFSSL* ssl) if (esSz > MAX_PSK_ID_LEN) { ERROR_OUT(CLIENT_ID_ERROR, exit_scke); } + /* CLIENT: Pre-shared Key for peer authentication. */ + ssl->options.peerAuthGood = 1; /* place size and identity in output buffer sz:identity */ c16toa((word16)esSz, args->output); @@ -29218,6 +29260,8 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (!ssl->options.tls) ret = DeriveKeys(ssl); #endif + /* SERVER: peer auth based on session secret. */ + ssl->options.peerAuthGood = (ret == 0); ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE; return ret; @@ -29341,10 +29385,13 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (!ssl->options.tls) ret = DeriveKeys(ssl); #endif + /* SERVER: peer auth based on session secret. */ + ssl->options.peerAuthGood = (ret == 0); ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE; } } + return ret; } @@ -30203,6 +30250,9 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, NULL #endif ); + /* SERVER: Data verified with certificate's public key. */ + ssl->options.peerAuthGood = ssl->options.havePeerCert && + (ret == 0); } #endif /* HAVE_ECC */ #if defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH) @@ -30219,6 +30269,9 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, NULL #endif ); + /* SERVER: Data verified with certificate's public key. */ + ssl->options.peerAuthGood = ssl->options.havePeerCert && + (ret == 0); } #endif /* HAVE_ED25519 && !NO_ED25519_CLIENT_AUTH */ #if defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH) @@ -30235,6 +30288,9 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, NULL #endif ); + /* SERVER: Data verified with certificate's public key. */ + ssl->options.peerAuthGood = ssl->options.havePeerCert && + (ret == 0); } #endif /* HAVE_ED448 && !NO_ED448_CLIENT_AUTH */ @@ -30310,12 +30366,13 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (args->sendSz != args->sigSz || !args->output || XMEMCMP(args->output, encodedSig, - min(args->sigSz, MAX_ENCODED_SIG_SZ)) != 0) { + min(args->sigSz, MAX_ENCODED_SIG_SZ)) != 0) { ret = VERIFY_CERT_ERROR; } #ifdef WOLFSSL_SMALL_STACK - XFREE(encodedSig, ssl->heap, DYNAMIC_TYPE_SIGNATURE); + XFREE(encodedSig, ssl->heap, + DYNAMIC_TYPE_SIGNATURE); #endif } } @@ -30326,8 +30383,15 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ret = VERIFY_CERT_ERROR; } } + if (ret == 0) { + /* SERVER: Data verified with cert's public key. */ + ssl->options.peerAuthGood = ssl->options.havePeerCert && + (ret == 0); + } } #endif /* !NO_RSA */ + if (ret != 0) + break; /* Advance state and proceed */ ssl->options.asyncState = TLS_ASYNC_FINALIZE; @@ -31820,6 +31884,8 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], #endif ERROR_OUT(PSK_KEY_ERROR, exit_dcke); } + /* SERVER: Pre-shared Key for peer authentication. */ + ssl->options.peerAuthGood = 1; /* make psk pre master secret */ /* length of key + length 0s + length of key + key */ @@ -32659,6 +32725,8 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], #endif ERROR_OUT(PSK_KEY_ERROR, exit_dcke); } + /* SERVER: Pre-shared Key for peer authentication. */ + ssl->options.peerAuthGood = 1; c16toa((word16) ssl->arrays->psk_keySz, pms); pms += OPAQUE16_LEN; @@ -32695,6 +32763,8 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], ssl->arrays->psk_keySz > MAX_PSK_KEY_LEN) { ERROR_OUT(PSK_KEY_ERROR, exit_dcke); } + /* SERVER: Pre-shared Key for peer authentication. */ + ssl->options.peerAuthGood = 1; c16toa((word16) ssl->arrays->psk_keySz, pms); pms += OPAQUE16_LEN; diff --git a/src/keys.c b/src/keys.c index d08e59bff..7a8d417c9 100644 --- a/src/keys.c +++ b/src/keys.c @@ -2064,6 +2064,11 @@ int SetCipherSpecs(WOLFSSL* ssl) #endif #endif + if (ssl->specs.sig_algo == anonymous_sa_algo) { + /* CLIENT/SERVER: No peer authentication to be performed. */ + ssl->options.peerAuthGood = 1; + } + return 0; } diff --git a/src/ssl.c b/src/ssl.c index 57ef9e9f5..e9f1290fe 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -3168,6 +3168,8 @@ int wolfSSL_Rehandshake(WOLFSSL* ssl) ret = wolfSSL_UseSessionTicket(ssl); #endif } + /* CLIENT/SERVER: Reset peer authentication for full secure handshake. */ + ssl->options.peerAuthGood = 0; #ifdef HAVE_SESSION_TICKET if (ret == WOLFSSL_SUCCESS) @@ -14611,6 +14613,12 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, #if !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) case FIRST_REPLY_SECOND : + /* CLIENT: Fail-safe for Server Authentication. */ + if (!ssl->options.peerAuthGood) { + WOLFSSL_MSG("Server authentication did not happen"); + return WOLFSSL_FATAL_ERROR; + } + #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) if (ssl->options.sendVerify) { if ( (ssl->error = SendCertificateVerify(ssl)) != 0) { @@ -15007,6 +15015,10 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, return WOLFSSL_FATAL_ERROR; } } + else { + /* SERVER: Peer auth good if not verifying client. */ + ssl->options.peerAuthGood = 1; + } } #endif ssl->options.acceptState = CERT_REQ_SENT; @@ -15036,6 +15048,21 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, FALL_THROUGH; case ACCEPT_SECOND_REPLY_DONE : + #ifndef NO_CERTS + /* SERVER: When not resuming and verifying peer but no certificate + * received and not failing when not received then peer auth good. + */ + if (!ssl->options.resuming && ssl->options.verifyPeer && + !ssl->options.havePeerCert && !ssl->options.failNoCert) { + ssl->options.peerAuthGood = 1; + } + #endif /* !NO_CERTS */ + #ifdef WOLFSSL_NO_CLIENT_AUTH + if (!ssl->options.resuming) { + ssl->options.peerAuthGood = 1; + } + #endif + #ifdef HAVE_SESSION_TICKET if (ssl->options.createTicket && !ssl->options.noTicketTls12) { if ( (ssl->error = SendTicket(ssl)) != 0) { @@ -15049,6 +15076,12 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, FALL_THROUGH; case TICKET_SENT: + /* SERVER: Fail-safe for CLient Authentication. */ + if (!ssl->options.peerAuthGood) { + WOLFSSL_MSG("Client authentication did not happen"); + return WOLFSSL_FATAL_ERROR; + } + if ( (ssl->error = SendChangeCipher(ssl)) != 0) { WOLFSSL_ERROR(ssl->error); return WOLFSSL_FATAL_ERROR; diff --git a/src/tls.c b/src/tls.c index 4323c4f0a..719ccfa3c 100644 --- a/src/tls.c +++ b/src/tls.c @@ -5147,8 +5147,10 @@ static int TLSX_SessionTicket_Parse(WOLFSSL* ssl, const byte* input, ret = DoClientTicket(ssl, input, length); if (ret == WOLFSSL_TICKET_RET_OK) { /* use ticket to resume */ WOLFSSL_MSG("Using existing client ticket"); - ssl->options.useTicket = 1; - ssl->options.resuming = 1; + ssl->options.useTicket = 1; + ssl->options.resuming = 1; + /* SERVER: ticket is peer auth. */ + ssl->options.peerAuthGood = 1; } else if (ret == WOLFSSL_TICKET_RET_CREATE) { WOLFSSL_MSG("Using existing client ticket, creating new one"); ret = TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap); @@ -5159,6 +5161,8 @@ static int TLSX_SessionTicket_Parse(WOLFSSL* ssl, const byte* input, ssl->options.createTicket = 1; /* will send ticket msg */ ssl->options.useTicket = 1; ssl->options.resuming = 1; + /* SERVER: ticket is peer auth. */ + ssl->options.peerAuthGood = 1; } } else if (ret == WOLFSSL_TICKET_RET_REJECT) { WOLFSSL_MSG("Process client ticket rejected, not using"); diff --git a/src/tls13.c b/src/tls13.c index 2f41b7eec..0f6716e80 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -2781,6 +2781,10 @@ static int SetupPskKey(WOLFSSL* ssl, PreSharedKey* psk, int clientHello) 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 @@ -2903,6 +2907,11 @@ static int SetupPskKey(WOLFSSL* ssl, PreSharedKey* psk, int clientHello) psk->cipherSuite != suite[1])) { return PSK_KEY_ERROR; } + + if (!clientHello) { + /* CLIENT: using PSK for peer authentication. */ + ssl->options.peerAuthGood = 1; + } } #endif @@ -3629,7 +3638,6 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if ((ret = SetupPskKey(ssl, psk, 0)) != 0) return ret; ssl->options.pskNegotiated = 1; - ssl->options.peerAuthGood = 1; } #endif @@ -3961,6 +3969,8 @@ static int FindPsk(WOLFSSL* ssl, PreSharedKey* psk, byte* suite, int* err) if ((ret == 0) && found) { /* PSK negotiation has succeeded */ ssl->options.isPSK = 1; + /* SERVER: using PSK for peer authentication. */ + ssl->options.peerAuthGood = 1; } } @@ -4055,6 +4065,9 @@ static int DoPreSharedKeys(WOLFSSL* ssl, byte* suite, int* usingPSK, int* first) } #endif + /* SERVER: using secret in session ticket for peer auth. */ + ssl->options.peerAuthGood = 1; + #ifdef WOLFSSL_EARLY_DATA ssl->options.maxEarlyDataSz = ssl->session->maxEarlyDataSz; #endif @@ -4068,7 +4081,7 @@ static int DoPreSharedKeys(WOLFSSL* ssl, byte* suite, int* usingPSK, int* first) /* 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) { + ssl->session->ticketNonce.len, ssl->arrays->psk_key)) != 0) { return ret; } @@ -4894,7 +4907,6 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ssl->options.clientState = CLIENT_HELLO_COMPLETE; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) ssl->options.pskNegotiated = (args->usingPSK != 0); - ssl->options.peerAuthGood = ssl->options.pskNegotiated; #endif if (!args->usingPSK) { @@ -6744,6 +6756,9 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, ); 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; } @@ -6762,6 +6777,9 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, ); 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; @@ -6781,6 +6799,9 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, ); 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; @@ -6796,6 +6817,9 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, &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; @@ -6822,8 +6846,11 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, if (ret != 0) goto exit_dcv; - FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey); + /* CLIENT/SERVER: data verified with public key from + * certificate. */ ssl->peerRsaKeyPresent = 0; + FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey); + ssl->options.peerAuthGood = 1; } #endif /* !NO_RSA && WC_RSA_PSS */ @@ -6835,7 +6862,6 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, case TLS_ASYNC_FINALIZE: { ssl->options.havePeerVerify = 1; - ssl->options.peerAuthGood = 1; /* Set final index */ args->idx += args->sz; @@ -7965,10 +7991,6 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) return DUPLICATE_MSG_E; } ssl->msgsReceived.got_certificate = 1; - if (ssl->options.side == WOLFSSL_SERVER_END && - (!ssl->options.verifyPeer || !ssl->options.failNoCert)) { - ssl->options.peerAuthGood = 1; - } break; @@ -8732,6 +8754,7 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl) FALL_THROUGH; case FIRST_REPLY_SECOND: + /* CLIENT: check peer authentication. */ if (!ssl->options.peerAuthGood) { WOLFSSL_MSG("Server authentication did not happen"); return WOLFSSL_FATAL_ERROR; @@ -9683,6 +9706,7 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) } } else { + /* SERVER: Peer auth good if not verifying client. */ ssl->options.peerAuthGood = 1; } } @@ -9763,6 +9787,17 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) 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;