ssl_timers_unittest.cc (9647B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "secerr.h" 8 #include "ssl.h" 9 #include "sslerr.h" 10 #include "sslproto.h" 11 12 extern "C" { 13 // This is not something that should make you happy. 14 #include "libssl_internals.h" 15 } 16 17 #include "gtest_utils.h" 18 #include "nss_scoped_ptrs.h" 19 #include "tls_connect.h" 20 #include "tls_filter.h" 21 #include "tls_parser.h" 22 23 namespace nss_test { 24 25 // Reproducing https://bugzilla.mozilla.org/show_bug.cgi?id=1978603 26 // The test causes assertion failure: timer->cb == NULL, at 27 // lib/ssl/dtlscon.c:922 28 29 // The general problem in the bug was that each post-handshake message 30 // was starting a new retransmit timer. 31 // We've decided to change the timer logic such that a retransmit timer 32 // will be restarted when a new request for a timer arrives. 33 34 // This test ensures that if two different post-handshake messages are queued 35 // for retransmission, the timer behaves correctly. When the second message is 36 // queued, we reset the timer. This means a message never waits longer than the 37 // minimum timer delay. The first message queued will be retransmitted a bit 38 // more aggressively than it would otherwise, but this is unlikely to be a 39 // problem. 40 41 // We're currently supporting NewSessionTicker and KeyUpdate post-handshake 42 // messages. 43 44 // The filter will be dropping Key Update and New Session Ticket until disabled 45 // When it's disabled, it will keep track of the New Session Ticket messages 46 // sent by the server, and when it founds one - it will record the sequence 47 // number This sequence number will then be provided to the second filter that 48 // will be checking the list of ACKs seaching for this message 49 class TLSSessionTicketAndKUDropper : public TlsRecordFilter { 50 public: 51 TLSSessionTicketAndKUDropper(const std::shared_ptr<TlsAgent>& a) 52 : TlsRecordFilter(a), enabled_(true), sequenceNumberNST(0) {} 53 54 void disable() { enabled_ = false; } 55 56 uint64_t getSentSessionTicketSeqNum() { return sequenceNumberNST; } 57 58 protected: 59 PacketFilter::Action FilterRecord(const TlsRecordHeader& header, 60 const DataBuffer& record, size_t* offset, 61 DataBuffer* output) override { 62 if (!header.is_protected()) { 63 return KEEP; 64 } 65 66 uint16_t protection_epoch; 67 uint8_t inner_content_type; 68 DataBuffer plaintext; 69 TlsRecordHeader out_header; 70 71 if (!Unprotect(header, record, &protection_epoch, &inner_content_type, 72 &plaintext, &out_header)) { 73 return KEEP; 74 } 75 76 if (plaintext.data()[0] == ssl_hs_new_session_ticket) { 77 if (enabled_) { 78 return DROP; 79 } else { 80 sequenceNumberNST = out_header.sequence_number(); 81 } 82 } 83 84 if (plaintext.data()[0] == ssl_hs_key_update && enabled_) { 85 return DROP; 86 } 87 88 return KEEP; 89 } 90 91 private: 92 bool enabled_; 93 uint64_t sequenceNumberNST; 94 }; 95 96 class TLSACKRecorder : public TlsRecordFilter { 97 public: 98 TLSACKRecorder(const std::shared_ptr<TlsAgent>& a) 99 : TlsRecordFilter(a), 100 enabled_(false), 101 isSeqNumFound(false), 102 sequenceNumberToFindACKed(0) {} 103 104 void EnableTLSACKCatcherWithSeqNum(uint64_t seqNum) { 105 enabled_ = true; 106 sequenceNumberToFindACKed = seqNum; 107 } 108 109 bool isNSTACKFound() { return isSeqNumFound; } 110 111 protected: 112 PacketFilter::Action FilterRecord(const TlsRecordHeader& header, 113 const DataBuffer& record, size_t* offset, 114 DataBuffer* output) override { 115 if (!enabled_) { 116 return KEEP; 117 } 118 119 if (!header.is_protected()) { 120 return KEEP; 121 } 122 123 uint16_t protection_epoch; 124 uint8_t inner_content_type; 125 DataBuffer plaintext; 126 TlsRecordHeader out_header; 127 128 if (!Unprotect(header, record, &protection_epoch, &inner_content_type, 129 &plaintext, &out_header)) { 130 return KEEP; 131 } 132 133 if (plaintext.data() == NULL || plaintext.len() == 0) { 134 return KEEP; 135 } 136 137 if (decrypting() && inner_content_type != ssl_ct_ack) { 138 return KEEP; 139 } 140 141 uint8_t ack_message_header_len = 2; 142 143 uint8_t ack_message_len_one_ACK = 16; 144 size_t acks = plaintext.len() - ack_message_header_len; 145 EXPECT_EQ((uint64_t)0, acks % ack_message_len_one_ACK); 146 acks = acks / ack_message_len_one_ACK; 147 148 // struct { 149 // uint64 epoch; 150 // uint64 sequence_number; 151 // } RecordNumber; 152 153 // sequenceNumberToFindACKed has 16 bits for epoch and 48 for seqNum 154 uint64_t epoch = sequenceNumberToFindACKed >> 48; 155 uint64_t seqNum = sequenceNumberToFindACKed & 0xFFFFFFFFFFFF; 156 157 uint64_t lastByteEpoch = 0; 158 uint64_t leastByteSequence = 0; 159 160 for (size_t i = 0; i < acks; i++) { 161 // Here we check that the last byte of the epoch and the last byte of the 162 // sequence Because we just sent a couple of messages, so the values will 163 // be less than 256. 164 lastByteEpoch = plaintext.data()[2 + i * ack_message_len_one_ACK + 7]; 165 leastByteSequence = 166 plaintext.data()[2 + i * ack_message_len_one_ACK + 15]; 167 168 if ((epoch % 256 == lastByteEpoch) && 169 (seqNum % 256) == leastByteSequence) { 170 isSeqNumFound = true; 171 return KEEP; 172 } 173 } 174 return KEEP; 175 } 176 177 private: 178 bool enabled_; 179 bool isSeqNumFound; 180 uint64_t sequenceNumberToFindACKed; 181 }; 182 183 TEST_F(TlsConnectDatagram13, SendTicketThenKeyUpdate) { 184 ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); 185 Connect(); 186 187 SendReceive(); // Need to read so that we absorb the session tickets. 188 CheckKeys(); 189 190 // Resume the connection. 191 Reset(); 192 ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); 193 ExpectResumption(RESUME_TICKET); 194 195 auto filter = MakeTlsFilter<TLSSessionTicketAndKUDropper>(server_); 196 filter->EnableDecryption(); 197 198 auto ackRecorderFilter = MakeTlsFilter<TLSACKRecorder>(client_); 199 ackRecorderFilter->EnableDecryption(); 200 201 // This should cause sending a NewSessionTicket (thus, starting the timer) 202 // Sending the NewSessionTicket will be blocked by the filter 203 Connect(); 204 205 // Server sends Key Update 206 // The first Key Update will also be dropped by the server 207 EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), false)); 208 209 client_->ReadBytes(); 210 // Check that the client indeed has not received the KU. 211 SSLInt_SendImmediateACK(client_->ssl_fd()); 212 server_->ReadBytes(); 213 CheckEpochs(3, 3); 214 215 // Disabling dropping the message, the effective retransmit will start 216 filter->disable(); 217 218 // So, we don't have to wait until the next retransmit happens 219 ShiftDtlsTimers(); 220 server_->ReadBytes(); 221 // We get the Sequence number of the NewSessionTicket that the server has sent 222 uint64_t sequenceNumberNST = filter->getSentSessionTicketSeqNum(); 223 // And we check that the client has received (thus acked) the newly 224 // retransmitted NewSessionTicket 225 ackRecorderFilter->EnableTLSACKCatcherWithSeqNum(sequenceNumberNST); 226 227 // Client Received NewSessionTicker and KU 228 client_->ReadBytes(); 229 SSLInt_SendImmediateACK(client_->ssl_fd()); 230 server_->ReadBytes(); 231 232 // Client and Server both received and processed KU 233 CheckEpochs(3, 4); 234 235 // Client has successfully received and sent an ACK for NST message 236 EXPECT_EQ(true, ackRecorderFilter->isNSTACKFound()); 237 238 SendReceive(50); 239 } 240 241 class TLSIthMessageSeqNumDropper : public TlsRecordFilter { 242 public: 243 TLSIthMessageSeqNumDropper(const std::shared_ptr<TlsAgent>& a, 244 uint8_t messageSeqNumToDrop) 245 : TlsRecordFilter(a), 246 enabled_(true), 247 messageSeqNumToDrop_(messageSeqNumToDrop), 248 currentMessageSeqNum_(0) {} 249 250 void disable() { enabled_ = false; } 251 252 protected: 253 PacketFilter::Action FilterRecord(const TlsRecordHeader& header, 254 const DataBuffer& record, size_t* offset, 255 DataBuffer* output) override { 256 if (enabled_ && currentMessageSeqNum_ == messageSeqNumToDrop_) { 257 enabled_ = false; 258 259 uint16_t protection_epoch; 260 uint8_t inner_content_type; 261 DataBuffer plaintext; 262 TlsRecordHeader out_header; 263 264 if (!Unprotect(header, record, &protection_epoch, &inner_content_type, 265 &plaintext, &out_header)) { 266 return KEEP; 267 } 268 269 if (inner_content_type == ssl_ct_ack || 270 inner_content_type == ssl_ct_application_data) { 271 return KEEP; 272 } 273 274 return DROP; 275 } 276 277 currentMessageSeqNum_ += 1; 278 return KEEP; 279 } 280 281 private: 282 bool enabled_; 283 uint8_t messageSeqNumToDrop_; 284 uint8_t currentMessageSeqNum_; 285 }; 286 287 TEST_F(TlsConnectDatagram13, HandshakeDropIthMessageServer) { 288 uint8_t maxServerMessageSeq = 10; 289 290 for (uint8_t currMesSeqNum = 0; currMesSeqNum < maxServerMessageSeq; 291 currMesSeqNum++) { 292 EnsureTlsSetup(); 293 auto filter = 294 MakeTlsFilter<TLSIthMessageSeqNumDropper>(server_, currMesSeqNum); 295 filter->EnableDecryption(); 296 297 Connect(); 298 SendReceive(); 299 Reset(); 300 } 301 } 302 303 TEST_F(TlsConnectDatagram13, HandshakeDropIthMessageClient) { 304 uint8_t maxClientMessageSeq = 10; 305 306 for (uint8_t currMesSeqNum = 0; currMesSeqNum < maxClientMessageSeq; 307 currMesSeqNum++) { 308 EnsureTlsSetup(); 309 auto filter = 310 MakeTlsFilter<TLSIthMessageSeqNumDropper>(client_, currMesSeqNum); 311 filter->EnableDecryption(); 312 313 Connect(); 314 SendReceive(); 315 Reset(); 316 } 317 } 318 319 } // namespace nss_test