| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* vim: set ts=2 et sw=2 tw=80: */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include <functional> |
| #include <memory> |
| #include <vector> |
| #include "secerr.h" |
| #include "ssl.h" |
| #include "sslerr.h" |
| #include "sslproto.h" |
| |
| extern "C" { |
| // This is not something that should make you happy. |
| #include "libssl_internals.h" |
| } |
| |
| #include "gtest_utils.h" |
| #include "nss_scoped_ptrs.h" |
| #include "tls_connect.h" |
| #include "tls_filter.h" |
| #include "tls_parser.h" |
| |
| namespace nss_test { |
| |
| TEST_P(TlsConnectGeneric, SetupOnly) {} |
| |
| TEST_P(TlsConnectGeneric, Connect) { |
| SetExpectedVersion(std::get<1>(GetParam())); |
| Connect(); |
| CheckKeys(); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectEcdsa) { |
| SetExpectedVersion(std::get<1>(GetParam())); |
| Reset(TlsAgent::kServerEcdsa256); |
| Connect(); |
| CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); |
| } |
| |
| TEST_P(TlsConnectGeneric, CipherSuiteMismatch) { |
| EnsureTlsSetup(); |
| if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); |
| server_->EnableSingleCipher(TLS_AES_256_GCM_SHA384); |
| } else { |
| client_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); |
| server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); |
| } |
| ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); |
| client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); |
| server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP); |
| } |
| |
| class TlsAlertRecorder : public TlsRecordFilter { |
| public: |
| TlsAlertRecorder(const std::shared_ptr<TlsAgent>& a) |
| : TlsRecordFilter(a), level_(255), description_(255) {} |
| |
| PacketFilter::Action FilterRecord(const TlsRecordHeader& header, |
| const DataBuffer& input, |
| DataBuffer* output) override { |
| if (level_ != 255) { // Already captured. |
| return KEEP; |
| } |
| if (header.content_type() != ssl_ct_alert) { |
| return KEEP; |
| } |
| |
| std::cerr << "Alert: " << input << std::endl; |
| |
| TlsParser parser(input); |
| EXPECT_TRUE(parser.Read(&level_)); |
| EXPECT_TRUE(parser.Read(&description_)); |
| return KEEP; |
| } |
| |
| uint8_t level() const { return level_; } |
| uint8_t description() const { return description_; } |
| |
| private: |
| uint8_t level_; |
| uint8_t description_; |
| }; |
| |
| class HelloTruncator : public TlsHandshakeFilter { |
| public: |
| HelloTruncator(const std::shared_ptr<TlsAgent>& a) |
| : TlsHandshakeFilter( |
| a, {kTlsHandshakeClientHello, kTlsHandshakeServerHello}) {} |
| PacketFilter::Action FilterHandshake(const HandshakeHeader& header, |
| const DataBuffer& input, |
| DataBuffer* output) override { |
| output->Assign(input.data(), input.len() - 1); |
| return CHANGE; |
| } |
| }; |
| |
| // Verify that when NSS reports that an alert is sent, it is actually sent. |
| TEST_P(TlsConnectGeneric, CaptureAlertServer) { |
| MakeTlsFilter<HelloTruncator>(client_); |
| auto alert_recorder = MakeTlsFilter<TlsAlertRecorder>(server_); |
| |
| ConnectExpectAlert(server_, kTlsAlertDecodeError); |
| EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); |
| EXPECT_EQ(kTlsAlertDecodeError, alert_recorder->description()); |
| } |
| |
| TEST_P(TlsConnectGenericPre13, CaptureAlertClient) { |
| MakeTlsFilter<HelloTruncator>(server_); |
| auto alert_recorder = MakeTlsFilter<TlsAlertRecorder>(client_); |
| |
| ConnectExpectAlert(client_, kTlsAlertDecodeError); |
| EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); |
| EXPECT_EQ(kTlsAlertDecodeError, alert_recorder->description()); |
| } |
| |
| // In TLS 1.3, the server can't read the client alert. |
| TEST_P(TlsConnectTls13, CaptureAlertClient) { |
| MakeTlsFilter<HelloTruncator>(server_); |
| auto alert_recorder = MakeTlsFilter<TlsAlertRecorder>(client_); |
| |
| StartConnect(); |
| |
| client_->Handshake(); |
| client_->ExpectSendAlert(kTlsAlertDecodeError); |
| server_->Handshake(); |
| client_->Handshake(); |
| if (variant_ == ssl_variant_stream) { |
| // DTLS just drops the alert it can't decrypt. |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| } |
| server_->Handshake(); |
| EXPECT_EQ(kTlsAlertFatal, alert_recorder->level()); |
| EXPECT_EQ(kTlsAlertDecodeError, alert_recorder->description()); |
| } |
| |
| TEST_P(TlsConnectGenericPre13, ConnectFalseStart) { |
| client_->EnableFalseStart(); |
| Connect(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectAlpn) { |
| EnableAlpn(); |
| Connect(); |
| CheckAlpn("a"); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectAlpnPriorityA) { |
| // "alpn" "npn" |
| // alpn is the fallback here. npn has the highest priority and should be |
| // picked. |
| const std::vector<uint8_t> alpn = {0x04, 0x61, 0x6c, 0x70, 0x6e, |
| 0x03, 0x6e, 0x70, 0x6e}; |
| EnableAlpn(alpn); |
| Connect(); |
| CheckAlpn("npn"); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectAlpnPriorityB) { |
| // "alpn" "npn" "http" |
| // npn has the highest priority and should be picked. |
| const std::vector<uint8_t> alpn = {0x04, 0x61, 0x6c, 0x70, 0x6e, 0x03, 0x6e, |
| 0x70, 0x6e, 0x04, 0x68, 0x74, 0x74, 0x70}; |
| EnableAlpn(alpn); |
| Connect(); |
| CheckAlpn("npn"); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectAlpnClone) { |
| EnsureModelSockets(); |
| client_model_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_)); |
| server_model_->EnableAlpn(alpn_dummy_val_, sizeof(alpn_dummy_val_)); |
| Connect(); |
| CheckAlpn("a"); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectAlpnWithCustomCallbackA) { |
| // "ab" "alpn" |
| const std::vector<uint8_t> client_alpn = {0x02, 0x61, 0x62, 0x04, |
| 0x61, 0x6c, 0x70, 0x6e}; |
| EnableAlpnWithCallback(client_alpn, "alpn"); |
| Connect(); |
| CheckAlpn("alpn"); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectAlpnWithCustomCallbackB) { |
| // "ab" "alpn" |
| const std::vector<uint8_t> client_alpn = {0x02, 0x61, 0x62, 0x04, |
| 0x61, 0x6c, 0x70, 0x6e}; |
| EnableAlpnWithCallback(client_alpn, "ab"); |
| Connect(); |
| CheckAlpn("ab"); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectAlpnWithCustomCallbackC) { |
| // "cd" "npn" "alpn" |
| const std::vector<uint8_t> client_alpn = {0x02, 0x63, 0x64, 0x03, 0x6e, 0x70, |
| 0x6e, 0x04, 0x61, 0x6c, 0x70, 0x6e}; |
| EnableAlpnWithCallback(client_alpn, "npn"); |
| Connect(); |
| CheckAlpn("npn"); |
| } |
| |
| TEST_P(TlsConnectDatagram, ConnectSrtp) { |
| EnableSrtp(); |
| Connect(); |
| CheckSrtp(); |
| SendReceive(); |
| } |
| |
| TEST_P(TlsConnectGeneric, ConnectSendReceive) { |
| Connect(); |
| SendReceive(); |
| } |
| |
| class SaveTlsRecord : public TlsRecordFilter { |
| public: |
| SaveTlsRecord(const std::shared_ptr<TlsAgent>& a, size_t index) |
| : TlsRecordFilter(a), index_(index), count_(0), contents_() {} |
| |
| const DataBuffer& contents() const { return contents_; } |
| |
| protected: |
| PacketFilter::Action FilterRecord(const TlsRecordHeader& header, |
| const DataBuffer& data, |
| DataBuffer* changed) override { |
| if (count_++ == index_) { |
| contents_ = data; |
| } |
| return KEEP; |
| } |
| |
| private: |
| const size_t index_; |
| size_t count_; |
| DataBuffer contents_; |
| }; |
| |
| // Check that decrypting filters work and can read any record. |
| // This test (currently) only works in TLS 1.3 where we can decrypt. |
| TEST_F(TlsConnectStreamTls13, DecryptRecordClient) { |
| EnsureTlsSetup(); |
| // 0 = ClientHello, 1 = Finished, 2 = SendReceive, 3 = SendBuffer |
| auto saved = MakeTlsFilter<SaveTlsRecord>(client_, 3); |
| saved->EnableDecryption(); |
| Connect(); |
| SendReceive(); |
| |
| static const uint8_t data[] = {0xde, 0xad, 0xdc}; |
| DataBuffer buf(data, sizeof(data)); |
| client_->SendBuffer(buf); |
| EXPECT_EQ(buf, saved->contents()); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, DecryptRecordServer) { |
| EnsureTlsSetup(); |
| // Disable tickets so that we are sure to not get NewSessionTicket. |
| EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(), |
| SSL_ENABLE_SESSION_TICKETS, PR_FALSE)); |
| // 0 = ServerHello, 1 = other handshake, 2 = SendReceive, 3 = SendBuffer |
| auto saved = MakeTlsFilter<SaveTlsRecord>(server_, 3); |
| saved->EnableDecryption(); |
| Connect(); |
| SendReceive(); |
| |
| static const uint8_t data[] = {0xde, 0xad, 0xd5}; |
| DataBuffer buf(data, sizeof(data)); |
| server_->SendBuffer(buf); |
| EXPECT_EQ(buf, saved->contents()); |
| } |
| |
| class DropTlsRecord : public TlsRecordFilter { |
| public: |
| DropTlsRecord(const std::shared_ptr<TlsAgent>& a, size_t index) |
| : TlsRecordFilter(a), index_(index), count_(0) {} |
| |
| protected: |
| PacketFilter::Action FilterRecord(const TlsRecordHeader& header, |
| const DataBuffer& data, |
| DataBuffer* changed) override { |
| if (count_++ == index_) { |
| return DROP; |
| } |
| return KEEP; |
| } |
| |
| private: |
| const size_t index_; |
| size_t count_; |
| }; |
| |
| // Test that decrypting filters work correctly and are able to drop records. |
| TEST_F(TlsConnectStreamTls13, DropRecordServer) { |
| EnsureTlsSetup(); |
| // Disable session tickets so that the server doesn't send an extra record. |
| EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(), |
| SSL_ENABLE_SESSION_TICKETS, PR_FALSE)); |
| |
| // 0 = ServerHello, 1 = other handshake, 2 = first write |
| auto filter = MakeTlsFilter<DropTlsRecord>(server_, 2); |
| filter->EnableDecryption(); |
| Connect(); |
| server_->SendData(23, 23); // This should be dropped, so it won't be counted. |
| server_->ResetSentBytes(); |
| SendReceive(); |
| } |
| |
| TEST_F(TlsConnectStreamTls13, DropRecordClient) { |
| EnsureTlsSetup(); |
| // 0 = ClientHello, 1 = Finished, 2 = first write |
| auto filter = MakeTlsFilter<DropTlsRecord>(client_, 2); |
| filter->EnableDecryption(); |
| Connect(); |
| client_->SendData(26, 26); // This should be dropped, so it won't be counted. |
| client_->ResetSentBytes(); |
| SendReceive(); |
| } |
| |
| // Check that a server can use 0.5 RTT if client authentication isn't enabled. |
| TEST_P(TlsConnectTls13, WriteBeforeClientFinished) { |
| EnsureTlsSetup(); |
| StartConnect(); |
| client_->Handshake(); // ClientHello |
| server_->Handshake(); // ServerHello |
| |
| server_->SendData(10); |
| client_->ReadBytes(10); // Client should emit the Finished as a side-effect. |
| server_->Handshake(); // Server consumes the Finished. |
| CheckConnected(); |
| } |
| |
| // We don't allow 0.5 RTT if client authentication is requested. |
| TEST_P(TlsConnectTls13, WriteBeforeClientFinishedClientAuth) { |
| client_->SetupClientAuth(); |
| server_->RequestClientAuth(false); |
| StartConnect(); |
| client_->Handshake(); // ClientHello |
| server_->Handshake(); // ServerHello |
| |
| static const uint8_t data[] = {1, 2, 3}; |
| EXPECT_GT(0, PR_Write(server_->ssl_fd(), data, sizeof(data))); |
| EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); |
| |
| Handshake(); |
| CheckConnected(); |
| SendReceive(); |
| } |
| |
| // 0.5 RTT should fail with client authentication required. |
| TEST_P(TlsConnectTls13, WriteBeforeClientFinishedClientAuthRequired) { |
| client_->SetupClientAuth(); |
| server_->RequestClientAuth(true); |
| StartConnect(); |
| client_->Handshake(); // ClientHello |
| server_->Handshake(); // ServerHello |
| |
| static const uint8_t data[] = {1, 2, 3}; |
| EXPECT_GT(0, PR_Write(server_->ssl_fd(), data, sizeof(data))); |
| EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); |
| |
| Handshake(); |
| CheckConnected(); |
| SendReceive(); |
| } |
| |
| // The next two tests takes advantage of the fact that we |
| // automatically read the first 1024 bytes, so if |
| // we provide 1200 bytes, they overrun the read buffer |
| // provided by the calling test. |
| |
| // DTLS should return an error. |
| TEST_P(TlsConnectDatagram, ShortRead) { |
| Connect(); |
| client_->ExpectReadWriteError(); |
| server_->SendData(50, 50); |
| client_->ReadBytes(20); |
| EXPECT_EQ(0U, client_->received_bytes()); |
| EXPECT_EQ(SSL_ERROR_RX_SHORT_DTLS_READ, PORT_GetError()); |
| |
| // Now send and receive another packet. |
| server_->ResetSentBytes(); // Reset the counter. |
| SendReceive(); |
| } |
| |
| // TLS should get the write in two chunks. |
| TEST_P(TlsConnectStream, ShortRead) { |
| // This test behaves oddly with TLS 1.0 because of 1/n+1 splitting, |
| // so skip in that case. |
| if (version_ < SSL_LIBRARY_VERSION_TLS_1_1) GTEST_SKIP(); |
| |
| Connect(); |
| server_->SendData(50, 50); |
| // Read the first tranche. |
| client_->ReadBytes(20); |
| ASSERT_EQ(20U, client_->received_bytes()); |
| // The second tranche should now immediately be available. |
| client_->ReadBytes(); |
| ASSERT_EQ(50U, client_->received_bytes()); |
| } |
| |
| // We enable compression via the API but it's disabled internally, |
| // so we should never get it. |
| TEST_P(TlsConnectGeneric, ConnectWithCompressionEnabled) { |
| EnsureTlsSetup(); |
| client_->SetOption(SSL_ENABLE_DEFLATE, PR_TRUE); |
| server_->SetOption(SSL_ENABLE_DEFLATE, PR_TRUE); |
| Connect(); |
| EXPECT_FALSE(client_->is_compressed()); |
| SendReceive(); |
| } |
| |
| class TlsHolddownTest : public TlsConnectDatagram { |
| protected: |
| // This causes all timers to run to completion. It advances the clock and |
| // handshakes on both peers until both peers have no more timers pending, |
| // which should happen at the end of a handshake. This is necessary to ensure |
| // that the relatively long holddown timer expires, but that any other timers |
| // also expire and run correctly. |
| void RunAllTimersDown() { |
| while (true) { |
| PRIntervalTime time; |
| SECStatus rv = DTLS_GetHandshakeTimeout(client_->ssl_fd(), &time); |
| if (rv != SECSuccess) { |
| rv = DTLS_GetHandshakeTimeout(server_->ssl_fd(), &time); |
| if (rv != SECSuccess) { |
| break; // Neither peer has an outstanding timer. |
| } |
| } |
| |
| if (g_ssl_gtest_verbose) { |
| std::cerr << "Shifting timers" << std::endl; |
| } |
| ShiftDtlsTimers(); |
| Handshake(); |
| } |
| } |
| }; |
| |
| TEST_P(TlsHolddownTest, TestDtlsHolddownExpiry) { |
| Connect(); |
| std::cerr << "Expiring holddown timer" << std::endl; |
| RunAllTimersDown(); |
| SendReceive(); |
| if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { |
| // One for send, one for receive. |
| EXPECT_EQ(2, SSLInt_CountCipherSpecs(client_->ssl_fd())); |
| } |
| } |
| |
| TEST_P(TlsHolddownTest, TestDtlsHolddownExpiryResumption) { |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| Connect(); |
| SendReceive(); |
| |
| Reset(); |
| ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); |
| ExpectResumption(RESUME_TICKET); |
| Connect(); |
| RunAllTimersDown(); |
| SendReceive(); |
| // One for send, one for receive. |
| EXPECT_EQ(2, SSLInt_CountCipherSpecs(client_->ssl_fd())); |
| } |
| |
| class TlsPreCCSHeaderInjector : public TlsRecordFilter { |
| public: |
| TlsPreCCSHeaderInjector(const std::shared_ptr<TlsAgent>& a) |
| : TlsRecordFilter(a) {} |
| virtual PacketFilter::Action FilterRecord( |
| const TlsRecordHeader& record_header, const DataBuffer& input, |
| size_t* offset, DataBuffer* output) override { |
| if (record_header.content_type() != ssl_ct_change_cipher_spec) { |
| return KEEP; |
| } |
| |
| std::cerr << "Injecting Finished header before CCS\n"; |
| const uint8_t hhdr[] = {kTlsHandshakeFinished, 0x00, 0x00, 0x0c}; |
| DataBuffer hhdr_buf(hhdr, sizeof(hhdr)); |
| TlsRecordHeader nhdr(record_header.variant(), record_header.version(), |
| ssl_ct_handshake, 0); |
| *offset = nhdr.Write(output, *offset, hhdr_buf); |
| *offset = record_header.Write(output, *offset, input); |
| return CHANGE; |
| } |
| }; |
| |
| TEST_P(TlsConnectStreamPre13, ClientFinishedHeaderBeforeCCS) { |
| MakeTlsFilter<TlsPreCCSHeaderInjector>(client_); |
| ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage); |
| client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT); |
| server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER); |
| } |
| |
| TEST_P(TlsConnectStreamPre13, ServerFinishedHeaderBeforeCCS) { |
| MakeTlsFilter<TlsPreCCSHeaderInjector>(server_); |
| StartConnect(); |
| ExpectAlert(client_, kTlsAlertUnexpectedMessage); |
| Handshake(); |
| EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); |
| client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER); |
| EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state()); |
| server_->Handshake(); // Make sure alert is consumed. |
| } |
| |
| TEST_P(TlsConnectTls13, UnknownAlert) { |
| Connect(); |
| server_->ExpectSendAlert(0xff, kTlsAlertWarning); |
| client_->ExpectReceiveAlert(0xff, kTlsAlertWarning); |
| SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning, |
| 0xff); // Unknown value. |
| client_->ExpectReadWriteError(); |
| client_->WaitForErrorCode(SSL_ERROR_RX_UNKNOWN_ALERT, 2000); |
| } |
| |
| TEST_P(TlsConnectTls13, AlertWrongLevel) { |
| Connect(); |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage, kTlsAlertWarning); |
| client_->ExpectReceiveAlert(kTlsAlertUnexpectedMessage, kTlsAlertWarning); |
| SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning, |
| kTlsAlertUnexpectedMessage); |
| client_->ExpectReadWriteError(); |
| client_->WaitForErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT, 2000); |
| } |
| |
| TEST_P(TlsConnectTls13, UnknownRecord) { |
| static const uint8_t kUknownRecord[] = { |
| 0xff, SSL_LIBRARY_VERSION_TLS_1_2 >> 8, |
| SSL_LIBRARY_VERSION_TLS_1_2 & 0xff, 0, 0}; |
| |
| Connect(); |
| if (variant_ == ssl_variant_stream) { |
| // DTLS just drops the record with an invalid type. |
| server_->ExpectSendAlert(kTlsAlertUnexpectedMessage); |
| } |
| client_->SendDirect(DataBuffer(kUknownRecord, sizeof(kUknownRecord))); |
| server_->ExpectReadWriteError(); |
| server_->ReadBytes(); |
| if (variant_ == ssl_variant_stream) { |
| EXPECT_EQ(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE, server_->error_code()); |
| } else { |
| EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, server_->error_code()); |
| } |
| } |
| |
| TEST_F(TlsConnectStreamTls13, Tls13FailedWriteSecondFlight) { |
| EnsureTlsSetup(); |
| StartConnect(); |
| client_->Handshake(); |
| server_->Handshake(); // Send first flight. |
| client_->adapter()->SetWriteError(PR_IO_ERROR); |
| client_->Handshake(); // This will get an error, but shouldn't crash. |
| client_->CheckErrorCode(SSL_ERROR_SOCKET_WRITE_FAILURE); |
| } |
| |
| TEST_P(TlsConnectDatagram, BlockedWrite) { |
| Connect(); |
| |
| // Mark the socket as blocked. |
| client_->adapter()->SetWriteError(PR_WOULD_BLOCK_ERROR); |
| static const uint8_t data[] = {1, 2, 3}; |
| int32_t rv = PR_Write(client_->ssl_fd(), data, sizeof(data)); |
| EXPECT_GT(0, rv); |
| EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); |
| |
| // Remove the write error and though the previous write failed, future reads |
| // and writes should just work as if it never happened. |
| client_->adapter()->SetWriteError(0); |
| SendReceive(); |
| } |
| |
| TEST_F(TlsConnectTest, ConnectSSLv3) { |
| ConfigureVersion(SSL_LIBRARY_VERSION_3_0); |
| EnableOnlyStaticRsaCiphers(); |
| Connect(); |
| CheckKeys(ssl_kea_rsa, ssl_grp_none, ssl_auth_rsa_decrypt, ssl_sig_none); |
| } |
| |
| TEST_F(TlsConnectTest, ConnectSSLv3ClientAuth) { |
| ConfigureVersion(SSL_LIBRARY_VERSION_3_0); |
| EnableOnlyStaticRsaCiphers(); |
| client_->SetupClientAuth(); |
| server_->RequestClientAuth(true); |
| Connect(); |
| CheckKeys(ssl_kea_rsa, ssl_grp_none, ssl_auth_rsa_decrypt, ssl_sig_none); |
| } |
| |
| static size_t ExpectedCbcLen(size_t in, size_t hmac = 20, size_t block = 16) { |
| // MAC-then-Encrypt expansion formula: |
| return ((in + hmac + (block - 1)) / block) * block; |
| } |
| |
| TEST_F(TlsConnectTest, OneNRecordSplitting) { |
| ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_0); |
| EnsureTlsSetup(); |
| ConnectWithCipherSuite(TLS_RSA_WITH_AES_128_CBC_SHA); |
| auto records = MakeTlsFilter<TlsRecordRecorder>(server_); |
| // This should be split into 1, 16384 and 20. |
| DataBuffer big_buffer; |
| big_buffer.Allocate(1 + 16384 + 20); |
| server_->SendBuffer(big_buffer); |
| ASSERT_EQ(3U, records->count()); |
| EXPECT_EQ(ExpectedCbcLen(1), records->record(0).buffer.len()); |
| EXPECT_EQ(ExpectedCbcLen(16384), records->record(1).buffer.len()); |
| EXPECT_EQ(ExpectedCbcLen(20), records->record(2).buffer.len()); |
| } |
| |
| // We can't test for randomness easily here, but we can test that we don't |
| // produce a zero value, or produce the same value twice. There are 5 values |
| // here: two ClientHello.random, two ServerHello.random, and one zero value. |
| // Matrix them and fail if any are the same. |
| TEST_P(TlsConnectGeneric, CheckRandoms) { |
| ConfigureSessionCache(RESUME_NONE, RESUME_NONE); |
| |
| static const size_t random_len = 32; |
| uint8_t crandom1[random_len], srandom1[random_len]; |
| uint8_t z[random_len] = {0}; |
| |
| auto ch = MakeTlsFilter<TlsHandshakeRecorder>(client_, ssl_hs_client_hello); |
| auto sh = MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_server_hello); |
| Connect(); |
| ASSERT_TRUE(ch->buffer().len() > (random_len + 2)); |
| ASSERT_TRUE(sh->buffer().len() > (random_len + 2)); |
| memcpy(crandom1, ch->buffer().data() + 2, random_len); |
| memcpy(srandom1, sh->buffer().data() + 2, random_len); |
| EXPECT_NE(0, memcmp(crandom1, srandom1, random_len)); |
| EXPECT_NE(0, memcmp(crandom1, z, random_len)); |
| EXPECT_NE(0, memcmp(srandom1, z, random_len)); |
| |
| Reset(); |
| ch = MakeTlsFilter<TlsHandshakeRecorder>(client_, ssl_hs_client_hello); |
| sh = MakeTlsFilter<TlsHandshakeRecorder>(server_, ssl_hs_server_hello); |
| Connect(); |
| ASSERT_TRUE(ch->buffer().len() > (random_len + 2)); |
| ASSERT_TRUE(sh->buffer().len() > (random_len + 2)); |
| const uint8_t* crandom2 = ch->buffer().data() + 2; |
| const uint8_t* srandom2 = sh->buffer().data() + 2; |
| |
| EXPECT_NE(0, memcmp(crandom2, srandom2, random_len)); |
| EXPECT_NE(0, memcmp(crandom2, z, random_len)); |
| EXPECT_NE(0, memcmp(srandom2, z, random_len)); |
| |
| EXPECT_NE(0, memcmp(crandom1, crandom2, random_len)); |
| EXPECT_NE(0, memcmp(crandom1, srandom2, random_len)); |
| EXPECT_NE(0, memcmp(srandom1, crandom2, random_len)); |
| EXPECT_NE(0, memcmp(srandom1, srandom2, random_len)); |
| } |
| |
| void FailOnCloseNotify(const PRFileDesc* fd, void* arg, const SSLAlert* alert) { |
| ADD_FAILURE() << "received alert " << alert->description; |
| } |
| |
| void CheckCloseNotify(const PRFileDesc* fd, void* arg, const SSLAlert* alert) { |
| *reinterpret_cast<bool*>(arg) = true; |
| EXPECT_EQ(close_notify, alert->description); |
| EXPECT_EQ(alert_warning, alert->level); |
| } |
| |
| TEST_P(TlsConnectGeneric, ShutdownOneSide) { |
| Connect(); |
| |
| // Setup to check alerts. |
| EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(server_->ssl_fd(), |
| FailOnCloseNotify, nullptr)); |
| EXPECT_EQ(SECSuccess, SSL_AlertReceivedCallback(client_->ssl_fd(), |
| FailOnCloseNotify, nullptr)); |
| |
| bool client_sent = false; |
| EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(client_->ssl_fd(), |
| CheckCloseNotify, &client_sent)); |
| bool server_received = false; |
| EXPECT_EQ(SECSuccess, |
| SSL_AlertReceivedCallback(server_->ssl_fd(), CheckCloseNotify, |
| &server_received)); |
| EXPECT_EQ(PR_SUCCESS, PR_Shutdown(client_->ssl_fd(), PR_SHUTDOWN_SEND)); |
| |
| // Make sure that the server reads out the close_notify. |
| uint8_t buf[10]; |
| EXPECT_EQ(0, PR_Read(server_->ssl_fd(), buf, sizeof(buf))); |
| |
| // Reading and writing should still work in the one open direction. |
| EXPECT_TRUE(client_sent); |
| EXPECT_TRUE(server_received); |
| server_->SendData(10, 10); |
| client_->ReadBytes(10); |
| |
| // Now close the other side and do the same checks. |
| bool server_sent = false; |
| EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(server_->ssl_fd(), |
| CheckCloseNotify, &server_sent)); |
| bool client_received = false; |
| EXPECT_EQ(SECSuccess, |
| SSL_AlertReceivedCallback(client_->ssl_fd(), CheckCloseNotify, |
| &client_received)); |
| EXPECT_EQ(PR_SUCCESS, PR_Shutdown(server_->ssl_fd(), PR_SHUTDOWN_SEND)); |
| |
| EXPECT_EQ(0, PR_Read(client_->ssl_fd(), buf, sizeof(buf))); |
| EXPECT_TRUE(server_sent); |
| EXPECT_TRUE(client_received); |
| } |
| |
| TEST_P(TlsConnectGeneric, ShutdownOneSideThenCloseTcp) { |
| Connect(); |
| |
| bool client_sent = false; |
| EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(client_->ssl_fd(), |
| CheckCloseNotify, &client_sent)); |
| bool server_received = false; |
| EXPECT_EQ(SECSuccess, |
| SSL_AlertReceivedCallback(server_->ssl_fd(), CheckCloseNotify, |
| &server_received)); |
| EXPECT_EQ(PR_SUCCESS, PR_Shutdown(client_->ssl_fd(), PR_SHUTDOWN_SEND)); |
| |
| // Make sure that the server reads out the close_notify. |
| uint8_t buf[10]; |
| EXPECT_EQ(0, PR_Read(server_->ssl_fd(), buf, sizeof(buf))); |
| |
| // Now simulate the underlying connection closing. |
| client_->adapter()->Reset(); |
| |
| // Now close the other side and see that things don't explode. |
| EXPECT_EQ(PR_SUCCESS, PR_Shutdown(server_->ssl_fd(), PR_SHUTDOWN_SEND)); |
| |
| EXPECT_GT(0, PR_Read(client_->ssl_fd(), buf, sizeof(buf))); |
| EXPECT_EQ(PR_NOT_CONNECTED_ERROR, PR_GetError()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| GenericStream, TlsConnectGeneric, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, |
| TlsConnectTestBase::kTlsVAll)); |
| INSTANTIATE_TEST_SUITE_P( |
| GenericDatagram, TlsConnectGeneric, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, |
| TlsConnectTestBase::kTlsV11Plus)); |
| |
| INSTANTIATE_TEST_SUITE_P(StreamOnly, TlsConnectStream, |
| TlsConnectTestBase::kTlsVAll); |
| INSTANTIATE_TEST_SUITE_P(DatagramOnly, TlsConnectDatagram, |
| TlsConnectTestBase::kTlsV11Plus); |
| INSTANTIATE_TEST_SUITE_P(DatagramHolddown, TlsHolddownTest, |
| TlsConnectTestBase::kTlsV11Plus); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| Pre12Stream, TlsConnectPre12, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, |
| TlsConnectTestBase::kTlsV10V11)); |
| INSTANTIATE_TEST_SUITE_P( |
| Pre12Datagram, TlsConnectPre12, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, |
| TlsConnectTestBase::kTlsV11)); |
| |
| INSTANTIATE_TEST_SUITE_P(Version12Only, TlsConnectTls12, |
| TlsConnectTestBase::kTlsVariantsAll); |
| #ifndef NSS_DISABLE_TLS_1_3 |
| INSTANTIATE_TEST_SUITE_P(Version13Only, TlsConnectTls13, |
| TlsConnectTestBase::kTlsVariantsAll); |
| #endif |
| |
| INSTANTIATE_TEST_SUITE_P( |
| Pre13Stream, TlsConnectGenericPre13, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, |
| TlsConnectTestBase::kTlsV10ToV12)); |
| INSTANTIATE_TEST_SUITE_P( |
| Pre13Datagram, TlsConnectGenericPre13, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, |
| TlsConnectTestBase::kTlsV11V12)); |
| INSTANTIATE_TEST_SUITE_P(Pre13StreamOnly, TlsConnectStreamPre13, |
| TlsConnectTestBase::kTlsV10ToV12); |
| |
| INSTANTIATE_TEST_SUITE_P(Version12Plus, TlsConnectTls12Plus, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll, |
| TlsConnectTestBase::kTlsV12Plus)); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| GenericStream, TlsConnectGenericResumption, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, |
| TlsConnectTestBase::kTlsVAll, |
| ::testing::Values(true, false))); |
| INSTANTIATE_TEST_SUITE_P( |
| GenericDatagram, TlsConnectGenericResumption, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, |
| TlsConnectTestBase::kTlsV11Plus, |
| ::testing::Values(true, false))); |
| |
| INSTANTIATE_TEST_SUITE_P( |
| GenericStream, TlsConnectGenericResumptionToken, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream, |
| TlsConnectTestBase::kTlsVAll)); |
| INSTANTIATE_TEST_SUITE_P( |
| GenericDatagram, TlsConnectGenericResumptionToken, |
| ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, |
| TlsConnectTestBase::kTlsV11Plus)); |
| |
| INSTANTIATE_TEST_SUITE_P(GenericDatagram, TlsConnectTls13ResumptionToken, |
| TlsConnectTestBase::kTlsVariantsAll); |
| |
| } // namespace nss_test |