169 lines
5.2 KiB
C++
169 lines
5.2 KiB
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"
|
|
|
|
#include "gtest_utils.h"
|
|
#include "nss_scoped_ptrs.h"
|
|
#include "tls_connect.h"
|
|
#include "tls_filter.h"
|
|
#include "tls_parser.h"
|
|
|
|
namespace nss_test {
|
|
|
|
// This class cuts every unencrypted handshake record into two parts.
|
|
class RecordFragmenter : public PacketFilter {
|
|
public:
|
|
RecordFragmenter(bool is_dtls13)
|
|
: is_dtls13_(is_dtls13), sequence_number_(0), splitting_(true) {}
|
|
|
|
private:
|
|
class HandshakeSplitter {
|
|
public:
|
|
HandshakeSplitter(bool is_dtls13, const DataBuffer& input,
|
|
DataBuffer* output, uint64_t* sequence_number)
|
|
: is_dtls13_(is_dtls13),
|
|
input_(input),
|
|
output_(output),
|
|
cursor_(0),
|
|
sequence_number_(sequence_number) {}
|
|
|
|
private:
|
|
void WriteRecord(TlsRecordHeader& record_header,
|
|
DataBuffer& record_fragment) {
|
|
TlsRecordHeader fragment_header(
|
|
record_header.variant(), record_header.version(),
|
|
record_header.content_type(), *sequence_number_);
|
|
++*sequence_number_;
|
|
if (::g_ssl_gtest_verbose) {
|
|
std::cerr << "Fragment: " << fragment_header << ' ' << record_fragment
|
|
<< std::endl;
|
|
}
|
|
cursor_ = fragment_header.Write(output_, cursor_, record_fragment);
|
|
}
|
|
|
|
bool SplitRecord(TlsRecordHeader& record_header, DataBuffer& record) {
|
|
TlsParser parser(record);
|
|
while (parser.remaining()) {
|
|
TlsHandshakeFilter::HandshakeHeader handshake_header;
|
|
DataBuffer handshake_body;
|
|
bool complete = false;
|
|
if (!handshake_header.Parse(&parser, record_header, DataBuffer(),
|
|
&handshake_body, &complete)) {
|
|
ADD_FAILURE() << "couldn't parse handshake header";
|
|
return false;
|
|
}
|
|
if (!complete) {
|
|
ADD_FAILURE() << "don't want to deal with fragmented messages";
|
|
return false;
|
|
}
|
|
|
|
DataBuffer record_fragment;
|
|
// We can't fragment handshake records that are too small.
|
|
if (handshake_body.len() < 2) {
|
|
handshake_header.Write(&record_fragment, 0U, handshake_body);
|
|
WriteRecord(record_header, record_fragment);
|
|
continue;
|
|
}
|
|
|
|
size_t cut = handshake_body.len() / 2;
|
|
handshake_header.WriteFragment(&record_fragment, 0U, handshake_body, 0U,
|
|
cut);
|
|
WriteRecord(record_header, record_fragment);
|
|
|
|
handshake_header.WriteFragment(&record_fragment, 0U, handshake_body,
|
|
cut, handshake_body.len() - cut);
|
|
WriteRecord(record_header, record_fragment);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
bool Split() {
|
|
TlsParser parser(input_);
|
|
while (parser.remaining()) {
|
|
TlsRecordHeader header;
|
|
DataBuffer record;
|
|
if (!header.Parse(is_dtls13_, 0, &parser, &record)) {
|
|
ADD_FAILURE() << "bad record header";
|
|
return false;
|
|
}
|
|
|
|
if (::g_ssl_gtest_verbose) {
|
|
std::cerr << "Record: " << header << ' ' << record << std::endl;
|
|
}
|
|
|
|
// Don't touch packets from a non-zero epoch. Leave these unmodified.
|
|
if ((header.sequence_number() >> 48) != 0ULL) {
|
|
cursor_ = header.Write(output_, cursor_, record);
|
|
continue;
|
|
}
|
|
|
|
// Just rewrite the sequence number (CCS only).
|
|
if (header.content_type() != ssl_ct_handshake) {
|
|
EXPECT_EQ(ssl_ct_change_cipher_spec, header.content_type());
|
|
WriteRecord(header, record);
|
|
continue;
|
|
}
|
|
|
|
if (!SplitRecord(header, record)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
bool is_dtls13_;
|
|
const DataBuffer& input_;
|
|
DataBuffer* output_;
|
|
size_t cursor_;
|
|
uint64_t* sequence_number_;
|
|
};
|
|
|
|
protected:
|
|
virtual PacketFilter::Action Filter(const DataBuffer& input,
|
|
DataBuffer* output) override {
|
|
if (!splitting_) {
|
|
return KEEP;
|
|
}
|
|
|
|
output->Allocate(input.len());
|
|
HandshakeSplitter splitter(is_dtls13_, input, output, &sequence_number_);
|
|
if (!splitter.Split()) {
|
|
// If splitting fails, we obviously reached encrypted packets.
|
|
// Stop splitting from that point onward.
|
|
splitting_ = false;
|
|
return KEEP;
|
|
}
|
|
|
|
return CHANGE;
|
|
}
|
|
|
|
private:
|
|
bool is_dtls13_;
|
|
uint64_t sequence_number_;
|
|
bool splitting_;
|
|
};
|
|
|
|
TEST_P(TlsConnectDatagram, FragmentClientPackets) {
|
|
bool is_dtls13 = version_ >= SSL_LIBRARY_VERSION_TLS_1_3;
|
|
client_->SetFilter(std::make_shared<RecordFragmenter>(is_dtls13));
|
|
Connect();
|
|
SendReceive();
|
|
}
|
|
|
|
TEST_P(TlsConnectDatagram, FragmentServerPackets) {
|
|
bool is_dtls13 = version_ >= SSL_LIBRARY_VERSION_TLS_1_3;
|
|
server_->SetFilter(std::make_shared<RecordFragmenter>(is_dtls13));
|
|
Connect();
|
|
SendReceive();
|
|
}
|
|
|
|
} // namespace nss_test
|