Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/gtests/ssl_gtest/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 9 kB image not shown  

Quelle  ssl_timers_unittest.cc

  Sprache: C
 

/* -*- 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 "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 {

// Reproducing https://bugzilla.mozilla.org/show_bug.cgi?id=1978603
// The test causes assertion failure: timer->cb == NULL, at
// lib/ssl/dtlscon.c:922

// The general problem in the bug was that each post-handshake message
// was starting a new retransmit timer.
// We've decided to change the timer logic such that a retransmit timer
// will be restarted when a new request for a timer arrives.

// This test ensures that if two different post-handshake messages are queued
// for retransmission, the timer behaves correctly. When the second message is
// queued, we reset the timer. This means a message never waits longer than the
// minimum timer delay. The first message queued will be retransmitted a bit
// more aggressively than it would otherwise, but this is unlikely to be a
// problem.

// We're currently supporting NewSessionTicker and KeyUpdate post-handshake
// messages.

// The filter will be dropping Key Update and New Session Ticket until disabled
// When it's disabled, it will keep track of the New Session Ticket messages
// sent by the server, and when it founds one - it will record the sequence
// number This sequence number will then be provided to the second filter that
// will be checking the list of ACKs seaching for this message
class TLSSessionTicketAndKUDropper : public TlsRecordFilter {
 public:
  TLSSessionTicketAndKUDropper(const std::shared_ptr<TlsAgent>& a)
      : TlsRecordFilter(a), enabled_(true), sequenceNumberNST(0) {}

  void disable() { enabled_ = false; }

  uint64_t getSentSessionTicketSeqNum() { return sequenceNumberNST; }

 protected:
  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                    const DataBuffer& record, size_t* offset,
                                    DataBuffer* output) override {
    if (!header.is_protected()) {
      return KEEP;
    }

    uint16_t protection_epoch;
    uint8_t inner_content_type;
    DataBuffer plaintext;
    TlsRecordHeader out_header;

    if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
                   &plaintext, &out_header)) {
      return KEEP;
    }

    if (plaintext.data() == NULL || plaintext.len() == 0) {
      return KEEP;
    }

    if (plaintext.data()[0] == ssl_hs_new_session_ticket) {
      if (enabled_) {
        return DROP;
      } else {
        sequenceNumberNST = out_header.sequence_number();
      }
    }

    if (plaintext.data()[0] == ssl_hs_key_update && enabled_) {
      return DROP;
    }

    return KEEP;
  }

 private:
  bool enabled_;
  uint64_t sequenceNumberNST;
};

class TLSACKRecorder : public TlsRecordFilter {
 public:
  TLSACKRecorder(const std::shared_ptr<TlsAgent>& a)
      : TlsRecordFilter(a),
        enabled_(false),
        isSeqNumFound(false),
        sequenceNumberToFindACKed(0) {}

  void EnableTLSACKCatcherWithSeqNum(uint64_t seqNum) {
    enabled_ = true;
    sequenceNumberToFindACKed = seqNum;
  }

  bool isNSTACKFound() { return isSeqNumFound; }

 protected:
  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                    const DataBuffer& record, size_t* offset,
                                    DataBuffer* output) override {
    if (!enabled_) {
      return KEEP;
    }

    if (!header.is_protected()) {
      return KEEP;
    }

    uint16_t protection_epoch;
    uint8_t inner_content_type;
    DataBuffer plaintext;
    TlsRecordHeader out_header;

    if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
                   &plaintext, &out_header)) {
      return KEEP;
    }

    if (plaintext.data() == NULL || plaintext.len() == 0) {
      return KEEP;
    }

    if (decrypting() && inner_content_type != ssl_ct_ack) {
      return KEEP;
    }

    uint8_t ack_message_header_len = 2;

    uint8_t ack_message_len_one_ACK = 16;
    size_t acks = plaintext.len() - ack_message_header_len;
    EXPECT_EQ((uint64_t)0, acks % ack_message_len_one_ACK);
    acks = acks / ack_message_len_one_ACK;

    // struct {
    //   uint64 epoch;
    //   uint64 sequence_number;
    // } RecordNumber;

    // sequenceNumberToFindACKed has 16 bits for epoch and 48 for seqNum
    uint64_t epoch = sequenceNumberToFindACKed >> 48;
    uint64_t seqNum = sequenceNumberToFindACKed & 0xFFFFFFFFFFFF;

    uint64_t lastByteEpoch = 0;
    uint64_t leastByteSequence = 0;

    for (size_t i = 0; i < acks; i++) {
      // Here we check that the last byte of the epoch and the last byte of the
      // sequence Because we just sent a couple of messages, so the values will
      // be less than 256.
      lastByteEpoch = plaintext.data()[2 + i * ack_message_len_one_ACK + 7];
      leastByteSequence =
          plaintext.data()[2 + i * ack_message_len_one_ACK + 15];

      if ((epoch % 256 == lastByteEpoch) &&
          (seqNum % 256) == leastByteSequence) {
        isSeqNumFound = true;
        return KEEP;
      }
    }
    return KEEP;
  }

 private:
  bool enabled_;
  bool isSeqNumFound;
  uint64_t sequenceNumberToFindACKed;
};

TEST_F(TlsConnectDatagram13, SendTicketThenKeyUpdate) {
  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
  Connect();

  SendReceive();  // Need to read so that we absorb the session tickets.
  CheckKeys();

  // Resume the connection.
  Reset();
  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
  ExpectResumption(RESUME_TICKET);

  auto filter = MakeTlsFilter<TLSSessionTicketAndKUDropper>(server_);
  filter->EnableDecryption();

  auto ackRecorderFilter = MakeTlsFilter<TLSACKRecorder>(client_);
  ackRecorderFilter->EnableDecryption();

  // This should cause sending a NewSessionTicket (thus, starting the timer)
  // Sending the NewSessionTicket will be blocked by the filter
  Connect();

  // Server sends Key Update
  // The first Key Update will also be dropped by the server
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), false));

  client_->ReadBytes();
  // Check that the client indeed has not received the KU.
  SSLInt_SendImmediateACK(client_->ssl_fd());
  server_->ReadBytes();
  CheckEpochs(33);

  // Disabling dropping the message, the effective retransmit will start
  filter->disable();

  // So, we don't have to wait until the next retransmit happens
  ShiftDtlsTimers();
  server_->ReadBytes();
  // We get the Sequence number of the NewSessionTicket that the server has sent
  uint64_t sequenceNumberNST = filter->getSentSessionTicketSeqNum();
  // And we check that the client has received (thus acked) the newly
  // retransmitted NewSessionTicket
  ackRecorderFilter->EnableTLSACKCatcherWithSeqNum(sequenceNumberNST);

  // Client Received NewSessionTicker and KU
  client_->ReadBytes();
  SSLInt_SendImmediateACK(client_->ssl_fd());
  server_->ReadBytes();

  // Client and Server both received and processed KU
  CheckEpochs(34);

  // Client has successfully received and sent an ACK for NST message
  EXPECT_EQ(true, ackRecorderFilter->isNSTACKFound());

  SendReceive(50);
}

class TLSIthMessageSeqNumDropper : public TlsRecordFilter {
 public:
  TLSIthMessageSeqNumDropper(const std::shared_ptr<TlsAgent>& a,
                             uint8_t messageSeqNumToDrop)
      : TlsRecordFilter(a),
        enabled_(true),
        messageSeqNumToDrop_(messageSeqNumToDrop),
        currentMessageSeqNum_(0) {}

  void disable() { enabled_ = false; }

 protected:
  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                    const DataBuffer& record, size_t* offset,
                                    DataBuffer* output) override {
    if (enabled_ && currentMessageSeqNum_ == messageSeqNumToDrop_) {
      enabled_ = false;

      uint16_t protection_epoch;
      uint8_t inner_content_type;
      DataBuffer plaintext;
      TlsRecordHeader out_header;

      if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
                     &plaintext, &out_header)) {
        return KEEP;
      }

      if (inner_content_type == ssl_ct_ack ||
          inner_content_type == ssl_ct_application_data) {
        return KEEP;
      }

      return DROP;
    }

    currentMessageSeqNum_ += 1;
    return KEEP;
  }

 private:
  bool enabled_;
  uint8_t messageSeqNumToDrop_;
  uint8_t currentMessageSeqNum_;
};

TEST_F(TlsConnectDatagram13, HandshakeDropIthMessageServer) {
  uint8_t maxServerMessageSeq = 10;

  for (uint8_t currMesSeqNum = 0; currMesSeqNum < maxServerMessageSeq;
       currMesSeqNum++) {
    EnsureTlsSetup();
    auto filter =
        MakeTlsFilter<TLSIthMessageSeqNumDropper>(server_, currMesSeqNum);
    filter->EnableDecryption();

    Connect();
    SendReceive();
    Reset();
  }
}

TEST_F(TlsConnectDatagram13, HandshakeDropIthMessageClient) {
  uint8_t maxClientMessageSeq = 10;

  for (uint8_t currMesSeqNum = 0; currMesSeqNum < maxClientMessageSeq;
       currMesSeqNum++) {
    EnsureTlsSetup();
    auto filter =
        MakeTlsFilter<TLSIthMessageSeqNumDropper>(client_, currMesSeqNum);
    filter->EnableDecryption();

    Connect();
    SendReceive();
    Reset();
  }
}

}  // namespace nss_test

Messung V0.5 in Prozent
C=86 H=93 G=89

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.