summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_certificate_compression_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/ssl_gtest/ssl_certificate_compression_unittest.cc')
-rw-r--r--security/nss/gtests/ssl_gtest/ssl_certificate_compression_unittest.cc1559
1 files changed, 1559 insertions, 0 deletions
diff --git a/security/nss/gtests/ssl_gtest/ssl_certificate_compression_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_certificate_compression_unittest.cc
new file mode 100644
index 0000000000..01a02502c1
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_certificate_compression_unittest.cc
@@ -0,0 +1,1559 @@
+/* -*- 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 {
+
+class TLSCertificateCompressionExtensionCatcher : public TlsExtensionFilter {
+ public:
+ TLSCertificateCompressionExtensionCatcher(const std::shared_ptr<TlsAgent>& a)
+ : TlsExtensionFilter(a),
+ received_compressed_certificate_extension_(false){};
+
+ PacketFilter::Action FilterExtension(uint16_t extension_type,
+ const DataBuffer& input,
+ DataBuffer* output) {
+ if (extension_type != ssl_certificate_compression_xtn) {
+ return KEEP;
+ }
+ received_compressed_certificate_extension_ = true;
+
+ /* struct {
+ * CertificateCompressionAlgorithm algorithms<2..2^8-2>;
+ * } CertificateCompressionAlgorithms;
+ */
+ uint32_t numberOfExtensions = input.data()[0];
+ algorithms = DataBuffer(&input.data()[1], numberOfExtensions);
+ return KEEP;
+ }
+
+ DataBuffer GetBufCompressionAlgs() { return algorithms; }
+
+ bool sawCertificateCompressionExtension() {
+ return received_compressed_certificate_extension_;
+ }
+
+ private:
+ DataBuffer algorithms;
+ bool received_compressed_certificate_extension_;
+};
+
+class TLSCertificateCompressionExtensionModifier : public TlsExtensionFilter {
+ public:
+ TLSCertificateCompressionExtensionModifier(const std::shared_ptr<TlsAgent>& a,
+ uint8_t byte, uint8_t value)
+ : TlsExtensionFilter(a), offset_(byte), value_(value){};
+
+ PacketFilter::Action FilterExtension(uint16_t extension_type,
+ const DataBuffer& input,
+ DataBuffer* output) {
+ if (extension_type != ssl_certificate_compression_xtn) {
+ return KEEP;
+ }
+
+ *output = input;
+ output->data()[offset_] = value_;
+ return CHANGE;
+ }
+
+ private:
+ uint8_t offset_;
+ uint8_t value_;
+};
+
+/* The function returns a reference to a message of the following Handshake
+ * Type. */
+uint64_t findPointerToHandshakeType(DataBuffer plaintext, SSLHandshakeType t) {
+ uint64_t skip = 0;
+ /* struct {
+ ** HandshakeType msg_type;
+ ** uint24 length;
+ ** select (Handshake.msg_type) {
+ ** case client_hello: ClientHello;...
+ ** };
+ ** } Handshake;
+ */
+ while (skip < plaintext.len() && plaintext.data()[skip] != t) {
+ skip = skip + 1 /* HandshakeType */ + 3 /* length */
+ + (plaintext.data()[skip + 1 /* Handshake.msg_type */] << 16) +
+ (plaintext.data()[skip + 2] << 8) + (plaintext.data()[skip + 3]);
+ }
+
+ return skip;
+}
+
+class TLSCertificateCompressionCertificateCatcher : public TlsRecordFilter {
+ public:
+ TLSCertificateCompressionCertificateCatcher(
+ const std::shared_ptr<TlsAgent>& a)
+ : TlsRecordFilter(a) {
+ received_compressed_certificate_ = false;
+ used_compression_algorithm_ = 0x0;
+ EnableDecryption();
+ }
+
+ bool sawCompressedCertificate() { return received_compressed_certificate_; }
+ uint16_t getCertCompressionAlg() { return used_compression_algorithm_; }
+ void unsetSawCompressedCertificate() {
+ received_compressed_certificate_ = false;
+ }
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ uint16_t protection_epoch = 0;
+ TlsRecordHeader out_header(header);
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ uint64_t skip =
+ findPointerToHandshakeType(plaintext, ssl_hs_compressed_certificate);
+ if (skip >= plaintext.len() ||
+ plaintext.data()[skip] != ssl_hs_compressed_certificate) {
+ return KEEP;
+ }
+
+ skip = skip + 1 /* HandshakeType */ + 3 /* length */;
+ if (skip + 1 >= plaintext.len()) {
+ return KEEP;
+ }
+ used_compression_algorithm_ =
+ (plaintext.data()[skip] << 8) + plaintext.data()[skip + 1];
+ received_compressed_certificate_ = true;
+ return KEEP;
+ }
+
+ private:
+ bool received_compressed_certificate_;
+ uint16_t used_compression_algorithm_;
+};
+
+class TLSCertificateToEncodedCertificateChanger : public TlsRecordFilter {
+ public:
+ TLSCertificateToEncodedCertificateChanger(const std::shared_ptr<TlsAgent>& a)
+ : TlsRecordFilter(a) {
+ EnableDecryption();
+ }
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ uint16_t protection_epoch = 0;
+ TlsRecordHeader out_header(header);
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ uint64_t skip = findPointerToHandshakeType(plaintext, ssl_hs_certificate);
+ if (skip >= plaintext.len() ||
+ plaintext.data()[skip] != ssl_hs_certificate) {
+ return KEEP;
+ }
+
+ DataBuffer plaintextEncodedCert(plaintext);
+ /* ssl_hs_certificate -> ssl_hs_compressed_certificate */
+ plaintextEncodedCert.data()[skip] = ssl_hs_compressed_certificate;
+ /* Next 3 bytes are length*/
+ uint32_t certificateLen = (plaintext.data()[skip + 1] << 16) +
+ (plaintext.data()[skip + 2] << 8) +
+ plaintext.data()[skip + 3];
+
+ /* Random Encoding*/
+ const uint8_t encodingId[2] = {0xff, 0x01};
+ /* struct {
+ CertificateCompressionAlgorithm algorithm;
+ uint24 uncompressed_length;
+ opaque compressed_certificate_message<1..2^24-1>;
+ } CompressedCertificate; */
+
+ plaintextEncodedCert.Write(skip + 1, certificateLen + 5,
+ 3); // 2 bytes for algorithm + 3 bytes for len
+ plaintextEncodedCert.Write(skip + 4, (const uint8_t*)&encodingId, 2);
+ /* the uncompressed_length (the same as we did not change the length of the
+ * cert) */
+ plaintextEncodedCert.Write(skip + 6, certificateLen, 3);
+ /* Copy the rest of the certificate. */
+ plaintextEncodedCert.Write(skip + 9,
+ (const uint8_t*)&plaintext.data()[skip + 4],
+ plaintext.len() - skip - 4);
+
+ DataBuffer ciphertext;
+ bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
+ plaintextEncodedCert, &ciphertext, &out_header);
+ EXPECT_TRUE(ok);
+ if (!ok) {
+ return KEEP;
+ }
+ *offset = out_header.Write(output, *offset, ciphertext);
+ return CHANGE;
+ }
+};
+
+/* Test encoding function. */
+static SECStatus SimpleXorCertCompEncode(const SECItem* input,
+ SECItem* output) {
+ SECITEM_CopyItem(NULL, output, input);
+ for (size_t i = 0; i < output->len; i++) {
+ output->data[i] ^= 0x55;
+ }
+ return SECSuccess;
+}
+
+/* Test decoding function. */
+static SECStatus SimpleXorCertCompDecode(const SECItem* input, SECItem* output,
+ size_t expectedLenDecodedCertificate) {
+ SECITEM_CopyItem(NULL, output, input);
+ for (size_t i = 0; i < output->len; i++) {
+ output->data[i] ^= 0x55;
+ }
+
+ return SECSuccess;
+}
+
+static SECStatus SimpleXorWithDifferentValueEncode(const SECItem* input,
+ SECItem* output) {
+ SECITEM_CopyItem(NULL, output, input);
+ for (size_t i = 0; i < output->len; i++) {
+ output->data[i] ^= 0x77;
+ }
+ return SECSuccess;
+}
+
+/* Test decoding function. */
+static SECStatus SimpleXorWithDifferentValueDecode(
+ const SECItem* input, SECItem* output,
+ size_t expectedLenDecodedCertificate) {
+ SECITEM_CopyItem(NULL, output, input);
+ for (size_t i = 0; i < output->len; i++) {
+ output->data[i] ^= 0x77;
+ }
+
+ return SECSuccess;
+}
+
+/* These tests are checking the behaviour
+ * using the different compression algorithms.
+ *
+ * struct {
+ * CertificateCompressionAlgorithm algorithms<2..2^8-2>;
+ * } CertificateCompressionAlgorithms;
+ *
+ * The "extension_data" field of this extension
+ * SHALL contain a CertificateCompressionAlgorithms value:
+ * enum {
+ * zlib(1),
+ * brotli(2),
+ * zstd(3),
+ * (65535)
+ * } CertificateCompressionAlgorithm;
+ */
+
+/* Algorithm number 0 is reserved. If we receive it, we ignore this algorithm:
+ * 1) We do not return a failure if we encountered it
+ * 2) If it was the only certificate compression algorithm, we consider that we
+ * did not negotiate the extension
+ * 3) If there were the other agorithms, the
+ * extension is negotiated if one of the other algorithms is supported by the
+ * both parties.
+ */
+
+/* We can not add an algorithm with empty encoding/decoding function. */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_CannotAddAlgorithmEmptyEncodingAndDecoding) {
+ EnsureTlsSetup();
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function", NULL, NULL};
+
+ EXPECT_EQ(SECFailure,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+}
+
+/* We can not add an algorithm with reserved id. */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_CannotAddAlgorithmWithReservedID) {
+ EnsureTlsSetup();
+ SSLCertificateCompressionAlgorithm t = {
+ 0, "test function", SimpleXorCertCompEncode, SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECFailure,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+}
+
+/* We can add an algorithm with the ID already existed.
+ * In this case the previous algorithm will be re-written.
+ */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_AddingAlreadyExistingAlg) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECFailure, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+}
+
+/* The test modifies the length of the compression certificates algorithms
+ * supported by a server. Each identifier of CertificateCompressionAlgorithm is
+ * 2 bytes, so the odd length is incorrect.
+ */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_LengthIsOdd) {
+ EnsureTlsSetup();
+ SSLCertificateCompressionAlgorithm alg_ff01 = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionExtensionModifier>(client_, 0, 1);
+ filterExtension->EnableDecryption();
+
+ ExpectAlert(client_, kTlsAlertDecodeError);
+ ConnectExpectAlert(server_, kTlsAlertDecodeError);
+
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+
+ EXPECT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+}
+
+/* The test checks that the extension is not negotiated if in the ClientHello
+ * the extension length is bigger than the actual length of the extension.
+ */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_LengthIsBiggerThanExpected) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ auto filterExtension =
+ /*But we specify 1 algorithm*/
+ MakeTlsFilter<TLSCertificateCompressionExtensionModifier>(client_, 0, 4);
+ filterExtension->EnableDecryption();
+
+ ExpectAlert(client_, kTlsAlertDecodeError);
+ ConnectExpectAlert(server_, kTlsAlertDecodeError);
+
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+
+ EXPECT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+}
+
+/* The test checks that the extension is not negotiated if in the ClientHello
+ * the extension length is smaller than the actual length of the extension.
+ */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_LengthIsSmallerThanExpected) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ SSLCertificateCompressionAlgorithm alg_ff02 = {0xff02, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff02));
+
+ auto filterExtension =
+ /* But we specify two algorithms*/
+ MakeTlsFilter<TLSCertificateCompressionExtensionModifier>(client_, 0, 2);
+ filterExtension->EnableDecryption();
+
+ ExpectAlert(client_, kTlsAlertDecodeError);
+ ConnectExpectAlert(server_, kTlsAlertDecodeError);
+
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+
+ EXPECT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ client_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+}
+
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ClientHelloUsedCompressedCertificate) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateCatcher>(server_);
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+ Connect();
+
+ EXPECT_TRUE(filterExtension->sawCompressedCertificate());
+}
+
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ClientAuthUsesTheServerPreferredAlg) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateCatcher>(client_);
+
+ SSLCertificateCompressionAlgorithm serverPreferableAlg = {
+ // for decompression
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ SSLCertificateCompressionAlgorithm clientPreferableAlg = {
+ 0xff02, "test function id ff02", SimpleXorWithDifferentValueEncode,
+ SimpleXorWithDifferentValueDecode};
+
+ /* The server wants to use serverPreferableAlg for decompression. */
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), serverPreferableAlg));
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), clientPreferableAlg));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), clientPreferableAlg));
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), serverPreferableAlg));
+
+ client_->SetupClientAuth();
+ server_->RequestClientAuth(true);
+
+ /* Client is sending the client certificate. */
+ Connect();
+
+ EXPECT_TRUE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+ EXPECT_TRUE(SSLInt_ExtensionNegotiated(client_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ uint16_t certCompressionAlg = filterExtension->getCertCompressionAlg();
+ EXPECT_EQ(certCompressionAlg, serverPreferableAlg.id);
+ EXPECT_TRUE(filterExtension->sawCompressedCertificate());
+}
+
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ServerReceivedUnexpectedEncodedCertificate) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateToEncodedCertificateChanger>(client_);
+ client_->SetupClientAuth();
+ server_->RequestClientAuth(true);
+
+ ExpectAlert(server_, kTlsAlertDecodeError);
+ StartConnect();
+
+ server_->SetServerKeyBits(client_->server_key_bits());
+ server_->Handshake();
+ client_->Handshake();
+
+ ASSERT_TRUE_WAIT((server_->state() != TlsAgent::STATE_CONNECTING), 5000);
+ ASSERT_EQ(TlsAgent::STATE_ERROR, server_->state());
+
+ server_->ExpectSendAlert(kTlsAlertCloseNotify);
+ client_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ server_->CheckErrorCode(SEC_ERROR_UNEXPECTED_COMPRESSED_CERTIFICATE);
+}
+
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_UnknownAlgorithmNoExtensionNegotiated) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ /* Server does not support the encoding algorithm, only client. */
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ Connect();
+ EXPECT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+}
+
+class TLSCertificateToEncodedCertificateCrasher : public TlsRecordFilter {
+ public:
+ TLSCertificateToEncodedCertificateCrasher(const std::shared_ptr<TlsAgent>& a)
+ : TlsRecordFilter(a) {
+ EnableDecryption();
+ }
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ uint16_t protection_epoch = 0;
+ TlsRecordHeader out_header(header);
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ uint64_t skip = findPointerToHandshakeType(plaintext, ssl_hs_key_update);
+ if (skip >= plaintext.len() ||
+ plaintext.data()[skip] != ssl_hs_key_update) {
+ return KEEP;
+ }
+
+ uint32_t ku_len = (plaintext.data()[skip + 1] << 16) +
+ (plaintext.data()[skip + 2] << 8) +
+ plaintext.data()[skip + 3];
+
+ DataBuffer plaintextEncodedCert(plaintext);
+ plaintextEncodedCert.Write(skip, &plaintext.data()[skip], ku_len + 1);
+
+ const uint8_t encodedCert[456] = {
+ 0x19, 0x00, 0x01, 0xc4, 0xff, 0x01, 0x00, 0x01, 0xbf, 0x00, 0x00, 0x01,
+ 0xbb, 0x00, 0x01, 0xb6, 0x30, 0x82, 0x01, 0xb2, 0x30, 0x82, 0x01, 0x1b,
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
+ 0x11, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06,
+ 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x33,
+ 0x31, 0x32, 0x31, 0x33, 0x31, 0x38, 0x31, 0x34, 0x31, 0x38, 0x5a, 0x17,
+ 0x0d, 0x33, 0x33, 0x31, 0x32, 0x31, 0x33, 0x31, 0x38, 0x31, 0x34, 0x31,
+ 0x38, 0x5a, 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x30, 0x81, 0x9f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81,
+ 0x81, 0x00, 0xb2, 0xe9, 0x2c, 0xe7, 0x25, 0xe2, 0x10, 0x83, 0x1c, 0xe5,
+ 0x22, 0xa2, 0x08, 0x55, 0x1a, 0xdf, 0x17, 0xc9, 0x8b, 0x23, 0x48, 0xef,
+ 0x45, 0x28, 0xe5, 0x8d, 0x6c, 0x37, 0xc0, 0x5e, 0x74, 0x39, 0x05, 0x01,
+ 0xde, 0x15, 0x17, 0x43, 0xa0, 0x78, 0x5e, 0x3e, 0x02, 0x05, 0xf4, 0x4c,
+ 0x61, 0x97, 0xb2, 0x01, 0x29, 0xf7, 0x61, 0xf0, 0x10, 0x47, 0x1d, 0x68,
+ 0x22, 0xf9, 0xef, 0xf2, 0x8c, 0x3b, 0xe5, 0x78, 0x5f, 0x58, 0xf5, 0x88,
+ 0xd0, 0xb9, 0xa6, 0xe0, 0x9b, 0x3e, 0x6f, 0x86, 0xf1, 0x9d, 0xe5, 0x34,
+ 0x78, 0xd6, 0xad, 0x6f, 0x6e, 0x38, 0x90, 0x88, 0x29, 0x45, 0x9d, 0xd8,
+ 0x6d, 0x12, 0x14, 0x9a, 0x87, 0x45, 0xb7, 0x9e, 0x6b, 0xe3, 0x98, 0xdf,
+ 0x65, 0xa9, 0xb6, 0x30, 0x53, 0xb6, 0xca, 0xed, 0x82, 0x18, 0x69, 0x30,
+ 0x4e, 0xda, 0x8e, 0x11, 0xc9, 0x98, 0xc6, 0x2e, 0xd8, 0xcd, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x1a, 0x30, 0x18, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x81,
+ 0x81, 0x00, 0x4f, 0x9b, 0x47, 0x31, 0xe7, 0x71, 0x11, 0x45, 0x44, 0xf6,
+ 0x17, 0x09, 0xb3, 0x32, 0x95, 0xaa, 0xe7, 0xd1, 0xec, 0xb4, 0x8b, 0xb6,
+ 0xd3, 0xb6, 0xb5, 0x30, 0x61, 0x5b, 0x5a, 0xfe, 0x0e, 0xb8, 0x1d, 0x72,
+ 0xdb, 0x80, 0xd2, 0xf6, 0xd5, 0xdc, 0xbe, 0xff, 0x99, 0x69, 0xb9, 0x5c,
+ 0x67, 0x18, 0xd4, 0xcb, 0xa8, 0xcf, 0x57, 0x60, 0x3d, 0xc8, 0x76, 0x5e,
+ 0xc0, 0xcb, 0x6f, 0x22, 0x1a, 0x2c, 0xe6, 0xd1, 0x0e, 0x59, 0xb7, 0x0d,
+ 0xef, 0x36, 0x6f, 0x57, 0xd3, 0x77, 0x7b, 0xab, 0x29, 0x00, 0x09, 0x87,
+ 0x0f, 0xf2, 0xfb, 0x59, 0xf5, 0x72, 0x86, 0x33, 0x2f, 0x2f, 0x16, 0x44,
+ 0x77, 0xed, 0x03, 0x11, 0x43, 0x29, 0x81, 0x07, 0xd1, 0x1a, 0xc2, 0xd5,
+ 0x78, 0xd8, 0xaa, 0x20, 0x9e, 0x3e, 0x69, 0xcd, 0x6c, 0x3b, 0xd3, 0x07,
+ 0x32, 0xa3, 0x12, 0x60, 0x01, 0x2e, 0xf4, 0x29, 0x7f, 0x47, 0x00, 0x00};
+ plaintextEncodedCert.Write(skip, encodedCert, 456);
+ plaintextEncodedCert.Write(skip + 456, &plaintext.data()[skip], ku_len + 1);
+
+ DataBuffer ciphertext;
+ bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
+ plaintextEncodedCert, &ciphertext, &out_header);
+ EXPECT_TRUE(ok);
+ if (!ok) {
+ return KEEP;
+ }
+ *offset = out_header.Write(output, *offset, ciphertext);
+ return CHANGE;
+ }
+};
+
+TEST_F(
+ TlsConnectStreamTls13,
+ CertificateCompression_ServerReceivedUnexpectedEncodedCertificate_PostAuth) {
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateToEncodedCertificateCrasher>(client_);
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+
+ server_->ExpectSendAlert(kTlsAlertDecodeError);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+ server_->SendData(50);
+ client_->ReadBytes(50);
+ client_->SendData(50);
+ server_->ExpectReadWriteError();
+ server_->ReadBytes(50);
+ server_->CheckErrorCode(SEC_ERROR_UNEXPECTED_COMPRESSED_CERTIFICATE);
+}
+
+/* Here the server first request a client certificate during the handshake,
+ but after the handshake it received another, encoded certificate. */
+TEST_F(
+ TlsConnectStreamTls13,
+ CertificateCompression_ServerReceivedUnexpectedEncodedCertificateAfterRequesting) {
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateToEncodedCertificateCrasher>(client_);
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ client_->SetupClientAuth();
+ server_->RequestClientAuth(true);
+
+ Connect();
+ EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
+
+ server_->ExpectSendAlert(kTlsAlertDecodeError);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+ server_->SendData(50);
+ client_->ReadBytes(50);
+ client_->SendData(50);
+ server_->ExpectReadWriteError();
+ server_->ReadBytes(50);
+ server_->CheckErrorCode(SEC_ERROR_UNEXPECTED_COMPRESSED_CERTIFICATE);
+}
+
+TEST_F(TlsConnectStreamTls13, CertificateCompression_OneCommonAlg) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ SSLCertificateCompressionAlgorithm alg_ff02 = {
+ 0xff02, "test function id ff02", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff02));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff02));
+
+ Connect();
+ EXPECT_TRUE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ SSLCertificateCompressionAlgorithm alg;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_GetCertificateCompressionAlgorithm(server_->ssl_fd(), &alg));
+ EXPECT_EQ(0xff02, alg.id);
+}
+
+/*
+ Test checking the correct behaviour of the preference choice.
+ In NSS, the priority is based on the order of the algorithms set up:
+
+ For the CertificateCompression_Preference case,
+ the client algorithm 0xff01 has the higher priority and the
+ 0xff03 algorithm has the lowest priority.
+
+ Then, for each of the advertised algorithms, the second party checks if there
+ is a support of this algorithm. In our case, the server supports algs 0xff01
+ and 0xff02.
+
+ But as the algorithms 0xff02 has the highest priority, it will be negotiated.
+*/
+
+TEST_F(TlsConnectStreamTls13, CertificateCompression_Preference) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ SSLCertificateCompressionAlgorithm alg_ff02 = {
+ 0xff02, "test function id ff02", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ SSLCertificateCompressionAlgorithm alg_ff03 = {
+ 0xff03, "test function id ff02", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ /* By sending a compress_certificate extension, the sender indicates to
+ the peer the certificate-compression algorithms it is willing to use
+ for decompression. */
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff03));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff02));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff02));
+
+ Connect();
+ EXPECT_TRUE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ SSLCertificateCompressionAlgorithm alg;
+ EXPECT_EQ(SECSuccess,
+ SSLInt_GetCertificateCompressionAlgorithm(server_->ssl_fd(), &alg));
+ EXPECT_EQ(alg_ff02.id, alg.id);
+}
+
+TEST_F(TlsConnectStreamTls13, CertificateCompression_SameIDDifferentAlgs) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ SSLCertificateCompressionAlgorithm alg_ff01_but_diffent_alg = {
+ 0xff01, "test function pretending to be id ff01",
+ SimpleXorWithDifferentValueEncode, SimpleXorWithDifferentValueDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01_but_diffent_alg));
+
+ ExpectAlert(client_, kTlsAlertDecodeError);
+ ConnectExpectAlert(server_, kTlsAlertDecodeError);
+
+ server_->ExpectSendAlert(kTlsAlertCloseNotify);
+ client_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ server_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+ client_->CheckErrorCode(SSL_ERROR_BAD_SERVER);
+
+ EXPECT_TRUE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+}
+
+/* This test ensures that if the supported algorithms between server and client
+ * are different, no extension is negotiated.
+ */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_NoCommonAlgs) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ SSLCertificateCompressionAlgorithm alg_ff02 = {0xff02, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff02));
+
+ Connect();
+ EXPECT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+}
+
+/* The user is trying to add more certificate compression algorithms than it is
+ * allowed. The maximum of algorithms is specified by
+ * MAX_SUPPORTED_CERTCOMPR_ALGS.
+ */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_TooManyAlgorithms) {
+ EnsureTlsSetup();
+
+ for (size_t i = 0; i < MAX_SUPPORTED_CERTIFICATE_COMPRESSION_ALGS; i++) {
+ SSLCertificateCompressionAlgorithm t = {
+ (SSLCertificateCompressionAlgorithmID)(i + 1), "test function",
+ SimpleXorCertCompEncode, SimpleXorCertCompDecode};
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ }
+
+ SSLCertificateCompressionAlgorithm t_last = {
+ (SSLCertificateCompressionAlgorithmID)(
+ MAX_SUPPORTED_CERTIFICATE_COMPRESSION_ALGS + 1),
+ "test function", SimpleXorCertCompEncode, SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECFailure, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), t_last));
+}
+
+/* The test checking that when we install a new compression mechanism, it is
+ * advertised.
+ */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_SameEncodingAsInCertificateExt) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ StartConnect();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionExtensionCatcher>(client_);
+ filterExtension->EnableDecryption();
+
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ Connect();
+
+ DataBuffer supportedAlgorithms = filterExtension->GetBufCompressionAlgs();
+ bool supportsEstablishedExtension = false;
+
+ for (size_t i = 0; i < supportedAlgorithms.len() / 2; i++) {
+ uint16_t alg = (supportedAlgorithms.data()[2 * i] << 8) +
+ supportedAlgorithms.data()[2 * i + 1];
+ supportsEstablishedExtension =
+ supportsEstablishedExtension || (alg == 0xff01);
+ }
+
+ EXPECT_TRUE(supportsEstablishedExtension);
+}
+
+/* If there is no certificate compression algorithm is possible,
+ * the extension is not sent.
+ */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ServerChecksEncodingNoneInstalled) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ StartConnect();
+
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionExtensionCatcher>(client_);
+ Connect();
+
+ EXPECT_FALSE(filterExtension->sawCertificateCompressionExtension());
+}
+
+/* RFC 8879
+ * This extension is only supported with TLS 1.3 [RFC8446] and newer;
+ * if TLS 1.2 [RFC5246] or earlier is negotiated,
+ * the peers MUST ignore this extension.
+ */
+TEST_P(TlsConnectGeneric, CertificateCompressionTLS12AndBelow) {
+ if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) GTEST_SKIP();
+ if (version_ < SSL_LIBRARY_VERSION_TLS_1_1) GTEST_SKIP();
+ StartConnect();
+
+ /* Adding the certificate compression extension.*/
+ const uint8_t empty_buf[] = {0x01, 0x00, 0x01};
+ DataBuffer empty(empty_buf, 3);
+ auto filter = MakeTlsFilter<TlsExtensionAppender>(
+ client_, kTlsHandshakeClientHello, 27, empty);
+
+ if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ filter->EnableDecryption();
+ }
+
+ ConnectExpectAlert(server_, kTlsAlertDecryptError);
+
+ EXPECT_FALSE(SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+ client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+}
+
+/* Test encoding function. Creates an encoded certificate of size 0. */
+static SECStatus SimpleXorCertCompEncode_returns_buffer_size_0(
+ const SECItem* input, SECItem* output) {
+ SECITEM_MakeItem(NULL, output, input->data, 0);
+ return SECSuccess;
+}
+
+/* The CompressedCertificate message is formed as follows:
+ * struct {
+ * CertificateCompressionAlgorithm algorithm;
+ * uint24 uncompressed_length;
+ * opaque compressed_certificate_message<1..2^24-1>;
+ * } CompressedCertificate;
+ */
+
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_CompressionFunctionCreatesABufferOfSize0) {
+ ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+ StartConnect();
+
+ SSLCertificateCompressionAlgorithm t = {
+ 0xff01, "test function", SimpleXorCertCompEncode_returns_buffer_size_0,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
+ server_->CheckErrorCode(SEC_ERROR_LIBRARY_FAILURE);
+}
+
+class TLSCertificateCompressionCertificateModifier : public TlsRecordFilter {
+ public:
+ TLSCertificateCompressionCertificateModifier(
+ const std::shared_ptr<TlsAgent>& a, uint64_t _byte, uint64_t _value)
+ : TlsRecordFilter(a),
+ offset_start_(_byte),
+ offset_finish_(0xffffffff),
+ value_(_value) {
+ EnableDecryption();
+ }
+ TLSCertificateCompressionCertificateModifier(
+ const std::shared_ptr<TlsAgent>& a, uint64_t _byteStart,
+ uint64_t _byteFinish, uint64_t _value)
+ : TlsRecordFilter(a),
+ offset_start_(_byteStart),
+ offset_finish_(_byteFinish),
+ value_(_value) {
+ EnableDecryption();
+ }
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ uint16_t protection_epoch = 0;
+ TlsRecordHeader out_header(header);
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ uint64_t skip =
+ findPointerToHandshakeType(plaintext, ssl_hs_compressed_certificate);
+ if (skip >= plaintext.len() ||
+ plaintext.data()[skip] != ssl_hs_compressed_certificate) {
+ return KEEP;
+ }
+
+ if (offset_finish_ == 0xffffffff) {
+ plaintext.data()[skip + offset_start_] = value_;
+ } else {
+ for (size_t i = offset_start_; i < offset_finish_ + 1; i++) {
+ plaintext.data()[skip + i] = value_;
+ }
+ }
+
+ DataBuffer ciphertext;
+ bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
+ plaintext, &ciphertext, &out_header);
+ EXPECT_TRUE(ok);
+ if (!ok) {
+ return KEEP;
+ }
+ *offset = out_header.Write(output, *offset, ciphertext);
+ return CHANGE;
+ }
+
+ private:
+ uint64_t offset_start_;
+ uint64_t offset_finish_;
+ uint8_t value_;
+};
+
+class TLSCertificateCompressionCertificateElongator : public TlsRecordFilter {
+ public:
+ TLSCertificateCompressionCertificateElongator(
+ const std::shared_ptr<TlsAgent>& a, uint64_t len)
+ : TlsRecordFilter(a), len_(len) {
+ EnableDecryption();
+ }
+
+ protected:
+ PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+ const DataBuffer& record, size_t* offset,
+ DataBuffer* output) override {
+ uint8_t inner_content_type;
+ DataBuffer plaintext;
+ uint16_t protection_epoch = 0;
+ TlsRecordHeader out_header(header);
+
+ if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
+ &plaintext, &out_header)) {
+ return KEEP;
+ }
+
+ uint64_t skip =
+ findPointerToHandshakeType(plaintext, ssl_hs_compressed_certificate);
+ if (skip >= plaintext.len() ||
+ plaintext.data()[skip] != ssl_hs_compressed_certificate) {
+ return KEEP;
+ }
+
+ plaintext.Write(plaintext.len(), (uint32_t)0, len_);
+
+ DataBuffer ciphertext;
+ bool ok = Protect(spec(protection_epoch), out_header, inner_content_type,
+ plaintext, &ciphertext, &out_header);
+ EXPECT_TRUE(ok);
+ if (!ok) {
+ return KEEP;
+ }
+ *offset = out_header.Write(output, *offset, ciphertext);
+ return CHANGE;
+ }
+
+ private:
+ uint64_t len_;
+};
+
+/* The CompressedCertificate message is formed as follows:
+ * struct {
+ * CertificateCompressionAlgorithm algorithm;
+ * uint24 uncompressed_length;
+ * opaque compressed_certificate_message<1..2^24-1>;
+ * } CompressedCertificate;
+ *
+ * algorithm:
+ * The algorithm used to compress the certificate.
+ * The algorithm MUST be one of the algorithms listed in the peer's
+ * compress_certificate extension.
+ *
+ * In the next test we modify the encoding used to encode the certificate to the
+ * one that the server did not advertise.
+ */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_ReceivedWrongAlgorithm) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateModifier>(server_, 0x5,
+ 0x2);
+
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ExpectAlert(client_, kTlsAlertIllegalParameter);
+ StartConnect();
+
+ client_->SetServerKeyBits(server_->server_key_bits());
+ client_->Handshake();
+ server_->Handshake();
+
+ ASSERT_TRUE_WAIT((client_->state() != TlsAgent::STATE_CONNECTING), 5000);
+ ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
+
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ client_->CheckErrorCode(
+ SEC_ERROR_CERTIFICATE_COMPRESSION_ALGORITHM_NOT_SUPPORTED);
+}
+
+static SECStatus SimpleXorCertCompDecode_length_smaller_than_given(
+ const SECItem* input, SECItem* output,
+ size_t expectedLenDecodedCertificate) {
+ SECITEM_MakeItem(NULL, output, input->data, input->len - 1);
+ return SECSuccess;
+}
+
+/*
+ * The next test modifies the length of the received certificate
+ * (uncompressed_length field of CompressedCertificate).
+ */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_ReceivedWrongLength) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateModifier>(server_, 0x6,
+ 0xff);
+ SSLCertificateCompressionAlgorithm t = {
+ 0xff01, "test function", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode_length_smaller_than_given};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ExpectAlert(client_, kTlsAlertBadCertificate);
+ StartConnect();
+
+ client_->SetServerKeyBits(server_->server_key_bits());
+ client_->Handshake();
+ server_->Handshake();
+
+ ASSERT_TRUE_WAIT((client_->state() != TlsAgent::STATE_CONNECTING), 5000);
+ ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
+
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERTIFICATE);
+}
+
+/* The next test modifies the length of the encoded certificate
+ * (compressed_certificate_message len);
+ * the new length is compressed_certificate_message is equal to 0.
+ */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ReceivedZeroCompressedMessage) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateModifier>(server_, 0xa,
+ 0xb, 0x0);
+
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ExpectAlert(client_, kTlsAlertBadCertificate);
+ StartConnect();
+
+ client_->SetServerKeyBits(server_->server_key_bits());
+ client_->Handshake();
+ server_->Handshake();
+
+ ASSERT_TRUE_WAIT((client_->state() != TlsAgent::STATE_CONNECTING), 5000);
+ ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
+
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERTIFICATE);
+}
+
+/* The next test modifies the length of the encoded certificate
+ * (compressed_certificate_message len);
+ * the new length is compressed_certificate_message is longer than the
+ * certificate.
+ */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ReceivedLongerCompressedMessage) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateModifier>(server_, 0x9,
+ 0xb, 0xff);
+
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ExpectAlert(client_, kTlsAlertBadCertificate);
+ StartConnect();
+
+ client_->SetServerKeyBits(server_->server_key_bits());
+ client_->Handshake();
+ server_->Handshake();
+
+ ASSERT_TRUE_WAIT((client_->state() != TlsAgent::STATE_CONNECTING), 5000);
+ ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
+
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERTIFICATE);
+}
+
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ReceivedCertificateTooLong) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateElongator>(server_,
+ 0x4);
+
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ExpectAlert(client_, kTlsAlertUnexpectedMessage);
+ StartConnect();
+ Handshake();
+
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+
+ client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE);
+}
+
+/* Test encoding function. Returns error unconditionally. */
+static SECStatus SimpleXorCertCompEncode_always_error(const SECItem* input,
+ SECItem* output) {
+ return SECFailure;
+}
+
+/* Test decoding function. Returns error unconditionally. */
+static SECStatus SimpleXorCertCompDecode_always_error(
+ const SECItem* input, SECItem* output,
+ size_t expectedLenDecodedCertificate) {
+ return SECFailure;
+}
+
+TEST_F(TlsConnectStreamTls13, CertificateCompression_CertificateCannotEncode) {
+ EnsureTlsSetup();
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode_always_error,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ExpectAlert(server_, kTlsAlertHandshakeFailure);
+ StartConnect();
+
+ client_->SetServerKeyBits(server_->server_key_bits());
+ client_->Handshake();
+ server_->Handshake();
+
+ ASSERT_TRUE_WAIT(client_->state() != TlsAgent::STATE_CONNECTING, 5000);
+
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+
+ server_->CheckErrorCode(SEC_ERROR_NO_MEMORY);
+}
+
+TEST_F(TlsConnectStreamTls13, CertificateCompression_CertificateCannotDecode) {
+ EnsureTlsSetup();
+
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode_always_error};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ ExpectAlert(client_, kTlsAlertBadCertificate);
+ StartConnect();
+
+ client_->SetServerKeyBits(server_->server_key_bits());
+ client_->Handshake();
+ server_->Handshake();
+
+ ASSERT_TRUE_WAIT(client_->state() != TlsAgent::STATE_CONNECTING, 5000);
+
+ server_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+ client_->ExpectSendAlert(kTlsAlertCloseNotify);
+
+ client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERTIFICATE);
+}
+
+/* The test checking the client authentification is successful using certificate
+ * compression. */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_PostAuth) {
+ EnsureTlsSetup();
+
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateCatcher>(client_);
+
+ SSLCertificateCompressionAlgorithm t = {0xff01, "test function",
+ SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(server_->ssl_fd(), t));
+ EXPECT_EQ(SECSuccess,
+ SSLExp_SetCertificateCompressionAlgorithm(client_->ssl_fd(), t));
+
+ SSLSignatureScheme scheme = ssl_sig_rsa_pss_rsae_sha256;
+ SECStatus rv = SSL_SignatureSchemePrefSet(server_->ssl_fd(), &scheme, 1);
+ EXPECT_EQ(SECSuccess, rv);
+ rv = SSL_SignatureSchemePrefSet(client_->ssl_fd(), &scheme, 1);
+ EXPECT_EQ(SECSuccess, rv);
+
+ client_->SetupClientAuth();
+ client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
+ size_t called = 0;
+ server_->SetAuthCertificateCallback(
+ [&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
+ called++;
+ return SECSuccess;
+ });
+ Connect();
+ // Send CertificateRequest.
+ EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+ << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+
+ // Need to do a round-trip so that the post-handshake message is
+ // handled on both client and server.
+ server_->SendData(50);
+ client_->ReadBytes(50);
+ client_->SendData(50);
+ server_->ReadBytes(50);
+
+ EXPECT_EQ(1U, called);
+ EXPECT_TRUE(SSLInt_ExtensionNegotiated(client_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ SendReceive(60);
+ client_->CheckClientAuthCompleted();
+
+ /* Ensuring that we used CompressedCertificate*/
+ EXPECT_TRUE(filterExtension->sawCompressedCertificate());
+}
+
+/* Partial decoding/encoding algorithms. */
+TEST_F(TlsConnectStreamTls13, CertificateCompression_ClientOnlyDecodes) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateCatcher>(server_);
+
+ SSLCertificateCompressionAlgorithm alg_only_encode = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode, NULL};
+
+ SSLCertificateCompressionAlgorithm alg_only_decode = {
+ 0xff01, "test function id ff01", NULL, SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_only_encode));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_only_decode));
+
+ Connect();
+
+ EXPECT_TRUE(filterExtension->sawCompressedCertificate());
+}
+
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_ClientOnlyDecodes_NoEncoding) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateCatcher>(client_);
+
+ SSLCertificateCompressionAlgorithm alg_only_encode = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode, NULL};
+
+ SSLCertificateCompressionAlgorithm alg_only_decode = {
+ 0xff01, "test function id ff01", NULL, SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_only_encode));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_only_decode));
+
+ SSLSignatureScheme scheme = ssl_sig_rsa_pss_rsae_sha256;
+ SECStatus rv = SSL_SignatureSchemePrefSet(server_->ssl_fd(), &scheme, 1);
+ EXPECT_EQ(SECSuccess, rv);
+ rv = SSL_SignatureSchemePrefSet(client_->ssl_fd(), &scheme, 1);
+ EXPECT_EQ(SECSuccess, rv);
+
+ client_->SetupClientAuth();
+ client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
+ size_t called = 0;
+ server_->SetAuthCertificateCallback(
+ [&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
+ called++;
+ return SECSuccess;
+ });
+ Connect();
+ // Send CertificateRequest.
+ EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+ << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+
+ // Need to do a round-trip so that the post-handshake message is
+ // handled on both client and server.
+ server_->SendData(50);
+ client_->ReadBytes(50);
+ client_->SendData(50);
+ server_->ReadBytes(50);
+
+ EXPECT_EQ(1U, called);
+ EXPECT_TRUE(SSLInt_ExtensionNegotiated(client_->ssl_fd(),
+ ssl_certificate_compression_xtn));
+
+ SendReceive(60);
+ client_->CheckClientAuthCompleted();
+
+ /* Ensuring that we have not used CompressedCertificate. */
+ EXPECT_FALSE(filterExtension->sawCompressedCertificate());
+}
+
+/* SSL_SendCertificateRequest function called by a server advertises
+ the Certificate Compression Extension. */
+TEST_F(TlsConnectStreamTls13,
+ CertificateCompression_TwoEncodedCertificateRequests) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateCatcher>(client_);
+
+ SSLCertificateCompressionAlgorithm alg_ff01 = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode,
+ SimpleXorCertCompDecode};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_ff01));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_ff01));
+
+ client_->SetupClientAuth();
+ client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
+ size_t called = 0;
+ server_->SetAuthCertificateCallback(
+ [&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
+ called++;
+ return SECSuccess;
+ });
+ Connect();
+ // Send CertificateRequest.
+ EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+ << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+
+ // Need to do a round-trip so that the post-handshake message is
+ // handled on both client and server.
+ server_->SendData(50);
+ client_->ReadBytes(50);
+ client_->SendData(50);
+ server_->ReadBytes(50);
+
+ EXPECT_EQ(1U, called);
+
+ SendReceive(60);
+ client_->CheckClientAuthCompleted();
+
+ EXPECT_TRUE(filterExtension->sawCompressedCertificate());
+ filterExtension->unsetSawCompressedCertificate();
+ EXPECT_FALSE(filterExtension->sawCompressedCertificate());
+
+ /* Advertising again the certificate encoding alg. */
+ EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
+ << "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
+
+ server_->SendData(50);
+ client_->ReadBytes(50);
+ client_->SendData(50);
+ server_->ReadBytes(50);
+
+ EXPECT_EQ(2U, called);
+ /* We saw the compressed certificate again. */
+ EXPECT_TRUE(filterExtension->sawCompressedCertificate());
+
+ SendReceive(200);
+ client_->CheckClientAuthCallbacksCompleted(2);
+}
+
+TEST_F(TlsConnectStreamTls13, CertificateCompression_ServerDecodingIsNULL) {
+ EnsureTlsSetup();
+ auto filterExtension =
+ MakeTlsFilter<TLSCertificateCompressionCertificateCatcher>(server_);
+
+ SSLCertificateCompressionAlgorithm alg_only_encode = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode, NULL};
+
+ SSLCertificateCompressionAlgorithm alg_only_decode = {
+ 0xff01, "test function id ff01", SimpleXorCertCompEncode, NULL};
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ server_->ssl_fd(), alg_only_encode));
+
+ EXPECT_EQ(SECSuccess, SSLExp_SetCertificateCompressionAlgorithm(
+ client_->ssl_fd(), alg_only_decode));
+
+ ExpectAlert(client_, kTlsAlertIllegalParameter);
+ ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
+
+ server_->ExpectSendAlert(kTlsAlertCloseNotify);
+ client_->ExpectReceiveAlert(kTlsAlertCloseNotify);
+
+ server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+ client_->CheckErrorCode(SEC_ERROR_LIBRARY_FAILURE);
+}
+
+} // namespace nss_test