diff options
author | Leander Schwarz <lschwarz@mozilla.com> | 2023-01-16 17:56:45 +0000 |
---|---|---|
committer | Leander Schwarz <lschwarz@mozilla.com> | 2023-01-16 17:56:45 +0000 |
commit | f1d7967e22830a08584bc88133932bef98ff622f (patch) | |
tree | e0e07aaa8fe6014b3f56fe53f53beda773abcb8a | |
parent | cf55859f44ab6f15a2e0d0c66c29c313e38b3070 (diff) | |
download | nss-hg-f1d7967e22830a08584bc88133932bef98ff622f.tar.gz |
Bug 1771100 - Added EarlyData ALPN test support to BoGo shim. r=djackson
Differential Revision: https://phabricator.services.mozilla.com/D157290
-rw-r--r-- | gtests/nss_bogo_shim/config.json | 4 | ||||
-rw-r--r-- | gtests/nss_bogo_shim/nss_bogo_shim.cc | 169 | ||||
-rw-r--r-- | lib/ssl/sslinfo.c | 29 | ||||
-rw-r--r-- | lib/ssl/sslt.h | 3 |
4 files changed, 151 insertions, 54 deletions
diff --git a/gtests/nss_bogo_shim/config.json b/gtests/nss_bogo_shim/config.json index bf32596cb..24aad553b 100644 --- a/gtests/nss_bogo_shim/config.json +++ b/gtests/nss_bogo_shim/config.json @@ -20,6 +20,7 @@ "V2ClientHello*":"Prefix data before V2 ClientHello leads to IO errors in NSS.", "Server-JDK11-NoWorkaround-3":"Unexpected Bogo crash.", "Resume-Server-UnofferedCipher-TLS13":"Bogo rejects resumption if client offers previously not used ciphersuites with equal hash algorithm (no 0Rtt).", + "EarlyData-FirstTicket-Server-TLS13":"Bogo provides specific early data logging which is the only check in this test but not supported by NSS.", "*Ed25519*":"Add Ed25519 support (Bug 1325335)", "*NoSSL3*":"Test passes but only because of handshake failure, NSS only rejects SSL3 immediately in TLS1.3 clients/servers.", @@ -47,7 +48,8 @@ "*ECH*RandomHRR*":"NSS sends real ECH in CH2 after receiving HRR rejcting ECH formally, Bogo expects instant ech_required alert. Bug 1779357", "*ECH*UnsolicitedInnerServerNameAck":"NSS always sends SNI in CHInner, Bogo tests if the client detects an unsolicited SNI in SH if CHInner did not include it. Bug 1781224", "CorruptTicket-TLS-TLS12":"NSS sends an alert on reception of a corrupted session ticket instead of falling back to full handshake. Bug 1783812", - "FalseStart-ALPN*":"TODO", + + "FalseStart-ALPN*":"TODO - Implementing TLS 1.2 only FalseStart has low priority.", "####################":"####################", "### TLS1/11 failures due to unsupported signature algorithms":"", diff --git a/gtests/nss_bogo_shim/nss_bogo_shim.cc b/gtests/nss_bogo_shim/nss_bogo_shim.cc index f653e3784..fb178d0a9 100644 --- a/gtests/nss_bogo_shim/nss_bogo_shim.cc +++ b/gtests/nss_bogo_shim/nss_bogo_shim.cc @@ -529,9 +529,42 @@ class TestAgent { return SECSuccess; } + SECStatus CheckALPN(std::string expectedALPN) { + SECStatus rv; + SSLNextProtoState state; + char chosen[256]; + unsigned int chosen_len; + + rv = SSL_GetNextProto(ssl_fd_.get(), &state, + reinterpret_cast<unsigned char*>(chosen), &chosen_len, + sizeof(chosen)); + if (rv != SECSuccess) { + PRErrorCode err = PR_GetError(); + std::cerr << "SSL_GetNextProto failed with error=" << FormatError(err) + << std::endl; + return SECFailure; + } + + assert(chosen_len <= sizeof(chosen)); + if (std::string(chosen, chosen_len) != expectedALPN) { + std::cerr << "Expexted ALPN (" << expectedALPN << ") != Choosen ALPN (" + << std::string(chosen, chosen_len) << ")" << std::endl; + return SECFailure; + } + + return SECSuccess; + } + + SECStatus AdvertiseALPN(std::string alpn) { + return SSL_SetNextProtoNego( + ssl_fd_.get(), reinterpret_cast<const unsigned char*>(alpn.c_str()), + alpn.size()); + } + SECStatus DoExchange(bool resuming) { SECStatus rv; int earlyDataSent = 0; + std::string str; sslSocket* ss = ssl_FindSocket(ssl_fd_.get()); if (!ss) { return SECFailure; @@ -549,11 +582,33 @@ class TestAgent { rv = SSLExp_SetClientEchConfigs(ssl_fd_.get(), bin, binLen); if (rv != SECSuccess) { PRErrorCode err = PR_GetError(); - std::cerr << "Setting up resuption ECH configs failed with error=" + std::cerr << "Setting up resumption ECH configs failed with error=" << err << FormatError(err) << std::endl; } free(bin); } + + str = cfg_.get<std::string>("on-resume-advertise-alpn"); + if (!str.empty()) { + if (AdvertiseALPN(str) != SECSuccess) { + PRErrorCode err = PR_GetError(); + std::cerr << "Setting up resumption ALPN failed with error=" << err + << FormatError(err) << std::endl; + } + } + } + + } else { /* Explicitly not on resume (on initial) */ + /* Client options */ + if (!cfg_.get<bool>("server")) { + str = cfg_.get<std::string>("on-initial-advertise-alpn"); + if (!str.empty()) { + if (AdvertiseALPN(str) != SECSuccess) { + PRErrorCode err = PR_GetError(); + std::cerr << "Setting up initial ALPN failed with error=" << err + << FormatError(err) << std::endl; + } + } } } @@ -571,30 +626,38 @@ class TestAgent { /* If the client is resuming. */ if (ss->statelessResume) { - /* If the client should send EarlyData. */ - if (cfg_.get<bool>("on-resume-shim-writes-first")) { - earlyDataSent = - ssl_SecureWrite(ss, kBogoDummyData, sizeof(kBogoDummyData)); - if (earlyDataSent < 0) { - std::cerr << "Sending of EarlyData failed" << std::endl; - return SECFailure; - } - } - SSLPreliminaryChannelInfo pinfo; rv = SSL_GetPreliminaryChannelInfo(ssl_fd_.get(), &pinfo, sizeof(SSLPreliminaryChannelInfo)); if (rv != SECSuccess) { PRErrorCode err = PR_GetError(); - std::cerr << "SSL_GetPreliminaryChannelInfo failed with error=" - << FormatError(err) << std::endl; + std::cerr << "SSL_GetPreliminaryChannelInfo failed with " << err + << std::endl; return SECFailure; } /* Check that the used ticket supports early data. */ if (cfg_.get<bool>("expect-ticket-supports-early-data")) { - if (!pinfo.canSendEarlyData) { + if (!pinfo.ticketSupportsEarlyData) { std::cerr << "Expected ticket to support EarlyData" << std::endl; + return SECFailure; + } + } + + /* If the client should send EarlyData. */ + if (cfg_.get<bool>("on-resume-shim-writes-first")) { + earlyDataSent = + ssl_SecureWrite(ss, kBogoDummyData, sizeof(kBogoDummyData)); + if (earlyDataSent < 0) { + std::cerr << "Sending of EarlyData failed" << std::endl; + return SECFailure; + } + } + + if (cfg_.get<bool>("expect-no-offer-early-data")) { + if (earlyDataSent) { + std::cerr << "Unexpectedly offered EarlyData" << std::endl; + return SECFailure; } } } @@ -679,28 +742,6 @@ class TestAgent { } } - auto alpn = cfg_.get<std::string>("expect-alpn"); - if (!alpn.empty()) { - SSLNextProtoState state; - char chosen[256]; - unsigned int chosen_len; - rv = SSL_GetNextProto(ssl_fd_.get(), &state, - reinterpret_cast<unsigned char*>(chosen), - &chosen_len, sizeof(chosen)); - if (rv != SECSuccess) { - PRErrorCode err = PR_GetError(); - std::cerr << "SSL_GetNextProto failed with error=" << FormatError(err) - << std::endl; - return SECFailure; - } - - assert(chosen_len <= sizeof(chosen)); - if (std::string(chosen, chosen_len) != alpn) { - std::cerr << "Unexpected ALPN selection" << std::endl; - return SECFailure; - } - } - SSLChannelInfo info; rv = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info)); if (rv != SECSuccess) { @@ -726,6 +767,21 @@ class TestAgent { } } + if (cfg_.get<bool>("expect-hrr")) { + if (!ss->ssl3.hs.helloRetry) { + std::cerr << "Expected HRR" << std::endl; + return SECFailure; + } + } + + str = cfg_.get<std::string>("expect-alpn"); + if (!str.empty()) { + if (CheckALPN(str) != SECSuccess) { + std::cerr << "Unexpected ALPN" << std::endl; + return SECFailure; + } + } + /* if resumed */ if (info.resumed) { if (cfg_.get<bool>("expect-session-miss")) { @@ -752,6 +808,27 @@ class TestAgent { return SECFailure; } } + + /* On successfully resumed connection. */ + if (info.earlyDataAccepted) { + str = cfg_.get<std::string>("on-resume-expect-alpn"); + if (!str.empty()) { + if (CheckALPN(str) != SECSuccess) { + std::cerr << "Unexpected ALPN on Resume" << std::endl; + return SECFailure; + } + } else { /* No real resume but new handshake on EarlyData rejection. */ + /* On Retry... */ + str = cfg_.get<std::string>("on-retry-expect-alpn"); + if (!str.empty()) { + if (CheckALPN(str) != SECSuccess) { + std::cerr << "Unexpected ALPN on HRR" << std::endl; + return SECFailure; + } + } + } + } + } else { /* Explicitly not on resume */ if (cfg_.get<bool>("on-initial-expect-ech-accept")) { if (!info.echAccepted) { @@ -759,12 +836,13 @@ class TestAgent { return SECFailure; } } - } - if (cfg_.get<bool>("expect-hrr")) { - if (!ss->ssl3.hs.helloRetry) { - std::cerr << "Expected HRR" << std::endl; - return SECFailure; + str = cfg_.get<std::string>("on-initial-expect-alpn"); + if (!str.empty()) { + if (CheckALPN(str) != SECSuccess) { + std::cerr << "Unexpected ALPN on Initial" << std::endl; + return SECFailure; + } } } @@ -801,7 +879,12 @@ std::unique_ptr<const Config> ReadConfig(int argc, char** argv) { cfg->AddEntry<bool>("is-handshaker-supported", false); cfg->AddEntry<std::string>("handshaker-path", ""); // Ignore this cfg->AddEntry<std::string>("advertise-alpn", ""); + cfg->AddEntry<std::string>("on-initial-advertise-alpn", ""); + cfg->AddEntry<std::string>("on-resume-advertise-alpn", ""); cfg->AddEntry<std::string>("expect-alpn", ""); + cfg->AddEntry<std::string>("on-initial-expect-alpn", ""); + cfg->AddEntry<std::string>("on-resume-expect-alpn", ""); + cfg->AddEntry<std::string>("on-retry-expect-alpn", ""); cfg->AddEntry<std::vector<int>>("signing-prefs", std::vector<int>()); cfg->AddEntry<std::vector<int>>("verify-prefs", std::vector<int>()); cfg->AddEntry<int>("expect-peer-signature-algorithm", 0); @@ -825,6 +908,10 @@ std::unique_ptr<const Config> ReadConfig(int argc, char** argv) { cfg->AddEntry<bool>("expect-no-ech-retry-configs", false); cfg->AddEntry<bool>("on-initial-expect-ech-accept", false); cfg->AddEntry<bool>("on-resume-expect-ech-accept", false); + cfg->AddEntry<bool>("expect-no-offer-early-data", false); + /* NSS does not support earlydata rejection reason logging => Ignore. */ + cfg->AddEntry<std::string>("on-resume-expect-early-data-reason", "none"); + cfg->AddEntry<std::string>("on-retry-expect-early-data-reason", "none"); auto rv = cfg->ParseArgs(argc, argv); switch (rv) { diff --git a/lib/ssl/sslinfo.c b/lib/ssl/sslinfo.c index d0f0701ae..fe32565c6 100644 --- a/lib/ssl/sslinfo.c +++ b/lib/ssl/sslinfo.c @@ -142,6 +142,7 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd, return SECFailure; } + /* All fields MUST be zero initialized! */ memset(&inf, 0, sizeof(inf)); inf.length = PR_MIN(sizeof(inf), len); @@ -154,19 +155,23 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc *fd, /* We shouldn't be able to send early data if the handshake is done. */ PORT_Assert(!ss->firstHsDone || !inf.canSendEarlyData); - if (ss->sec.ci.sid && - (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent || - ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted)) { - if (ss->statelessResume) { - inf.maxEarlyDataSize = - ss->sec.ci.sid->u.ssl3.locked.sessionTicket.max_early_data_size; - } else if (ss->psk) { - /* We may have cleared the handshake list, so check the socket. - * This is permissable since we only support one EPSK at a time. */ - inf.maxEarlyDataSize = ss->psk->maxEarlyData; + if (ss->sec.ci.sid) { + PRUint32 ticketMaxEarlyData = + ss->sec.ci.sid->u.ssl3.locked.sessionTicket.max_early_data_size; + + /* Resumption token info. */ + inf.ticketSupportsEarlyData = (ticketMaxEarlyData > 0); + + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent || + ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) { + if (ss->statelessResume) { + inf.maxEarlyDataSize = ticketMaxEarlyData; + } else if (ss->psk) { + /* We may have cleared the handshake list, so check the socket. + * This is permissable since we only support one EPSK at a time. */ + inf.maxEarlyDataSize = ss->psk->maxEarlyData; + } } - } else { - inf.maxEarlyDataSize = 0; } inf.zeroRttCipherSuite = ss->ssl3.hs.zeroRttSuite; diff --git a/lib/ssl/sslt.h b/lib/ssl/sslt.h index 59e82e34d..95f4d744a 100644 --- a/lib/ssl/sslt.h +++ b/lib/ssl/sslt.h @@ -447,6 +447,9 @@ typedef struct SSLPreliminaryChannelInfoStr { * should use the following hostname extracted from the ECHConfig. */ const char* echPublicName; + /* The following field was added in NSS 3.85. */ + PRBool ticketSupportsEarlyData; + /* When adding new fields to this structure, please document the * NSS version in which they were added. */ } SSLPreliminaryChannelInfo; |