summaryrefslogtreecommitdiffstats
path: root/src/ssl/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssl/test')
-rw-r--r--src/ssl/test/CMakeLists.txt3
-rw-r--r--src/ssl/test/bssl_shim.cc70
-rw-r--r--src/ssl/test/malloc.cc139
-rw-r--r--src/ssl/test/runner/common.go34
-rw-r--r--src/ssl/test/runner/conn.go16
-rw-r--r--src/ssl/test/runner/dtls.go68
-rw-r--r--src/ssl/test/runner/handshake_client.go27
-rw-r--r--src/ssl/test/runner/handshake_server.go28
-rw-r--r--src/ssl/test/runner/key_agreement.go17
-rw-r--r--src/ssl/test/runner/runner.go700
-rw-r--r--src/ssl/test/test_config.cc3
-rw-r--r--src/ssl/test/test_config.h3
12 files changed, 571 insertions, 537 deletions
diff --git a/src/ssl/test/CMakeLists.txt b/src/ssl/test/CMakeLists.txt
index a0d7a5e..3b07903 100644
--- a/src/ssl/test/CMakeLists.txt
+++ b/src/ssl/test/CMakeLists.txt
@@ -5,9 +5,10 @@ add_executable(
async_bio.cc
bssl_shim.cc
- malloc.cc
packeted_bio.cc
test_config.cc
+
+ $<TARGET_OBJECTS:test_support>
)
target_link_libraries(bssl_shim ssl crypto)
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 1cf96f2..40cb149 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -406,14 +406,6 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) {
return nullptr;
}
- if (config->is_dtls) {
- // DTLS needs read-ahead to function on a datagram BIO.
- //
- // TODO(davidben): this should not be necessary. DTLS code should only
- // expect a datagram BIO.
- SSL_CTX_set_read_ahead(ssl_ctx.get(), 1);
- }
-
if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) {
return nullptr;
}
@@ -599,6 +591,9 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
if (config->allow_unsafe_legacy_renegotiation) {
SSL_set_options(ssl.get(), SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
+ if (config->no_legacy_server_connect) {
+ SSL_clear_options(ssl.get(), SSL_OP_LEGACY_SERVER_CONNECT);
+ }
if (!config->expected_channel_id.empty()) {
SSL_enable_tls_channel_id(ssl.get());
}
@@ -660,8 +655,9 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
!SSL_set_cipher_list(ssl.get(), config->cipher.c_str())) {
return false;
}
- if (config->reject_peer_renegotiations) {
- SSL_set_reject_peer_renegotiations(ssl.get(), 1);
+ if (!config->reject_peer_renegotiations) {
+ /* Renegotiations are disabled by default. */
+ SSL_set_reject_peer_renegotiations(ssl.get(), 0);
}
int sock = Connect(config->port);
@@ -703,6 +699,11 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
}
}
+ if (SSL_get_current_cipher(ssl.get()) != nullptr) {
+ fprintf(stderr, "non-null cipher before handshake\n");
+ return false;
+ }
+
int ret;
if (config->implicit_handshake) {
if (config->is_server) {
@@ -722,6 +723,11 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
return false;
}
+ if (SSL_get_current_cipher(ssl.get()) == nullptr) {
+ fprintf(stderr, "null cipher after handshake\n");
+ return false;
+ }
+
if (is_resume &&
(!!SSL_session_reused(ssl.get()) == config->expect_session_miss)) {
fprintf(stderr, "session was%s reused\n",
@@ -834,30 +840,6 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
}
}
- if (config->renegotiate) {
- if (config->async) {
- fprintf(stderr, "-renegotiate is not supported with -async.\n");
- return false;
- }
- if (config->implicit_handshake) {
- fprintf(stderr, "-renegotiate is not supported with -implicit-handshake.\n");
- return false;
- }
-
- SSL_renegotiate(ssl.get());
-
- ret = SSL_do_handshake(ssl.get());
- if (ret != 1) {
- return false;
- }
-
- SSL_set_state(ssl.get(), SSL_ST_ACCEPT);
- ret = SSL_do_handshake(ssl.get());
- if (ret != 1) {
- return false;
- }
- }
-
if (config->export_keying_material > 0) {
std::vector<uint8_t> result(
static_cast<size_t>(config->export_keying_material));
@@ -874,6 +856,26 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
}
}
+ if (config->tls_unique) {
+ uint8_t tls_unique[16];
+ size_t tls_unique_len;
+ if (!SSL_get_tls_unique(ssl.get(), tls_unique, &tls_unique_len,
+ sizeof(tls_unique))) {
+ fprintf(stderr, "failed to get tls-unique\n");
+ return false;
+ }
+
+ if (tls_unique_len != 12) {
+ fprintf(stderr, "expected 12 bytes of tls-unique but got %u",
+ static_cast<unsigned>(tls_unique_len));
+ return false;
+ }
+
+ if (WriteAll(ssl.get(), tls_unique, tls_unique_len) < 0) {
+ return false;
+ }
+ }
+
if (config->write_different_record_sizes) {
if (config->is_dtls) {
fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");
diff --git a/src/ssl/test/malloc.cc b/src/ssl/test/malloc.cc
deleted file mode 100644
index 2ec5582..0000000
--- a/src/ssl/test/malloc.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-/* Copyright (c) 2014, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
-#include <openssl/base.h>
-
-#if defined(__has_feature)
-#if __has_feature(address_sanitizer)
-#define OPENSSL_ASAN
-#endif
-#endif
-
-// This file isn't built on ARM or Aarch64 because we link statically in those
-// builds and trying to override malloc in a static link doesn't work. It's also
-// disabled on ASan builds as this interferes with ASan's malloc interceptor.
-//
-// TODO(davidben): See if this and ASan's interceptors can be made to coexist.
-#if defined(__linux__) && !defined(OPENSSL_ARM) && \
- !defined(OPENSSL_AARCH64) && !defined(OPENSSL_ASAN)
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <new>
-
-
-/* This file defines overrides for the standard allocation functions that allow
- * a given allocation to be made to fail for testing. If the program is run
- * with MALLOC_NUMBER_TO_FAIL set to a base-10 number then that allocation will
- * return NULL. If MALLOC_ABORT_ON_FAIL is also defined then the allocation
- * will abort() rather than return NULL.
- *
- * This code is not thread safe. */
-
-static uint64_t current_malloc_count = 0;
-static uint64_t malloc_number_to_fail = 0;
-static char failure_enabled = 0, abort_on_fail = 0;
-static int in_call = 0;
-
-extern "C" {
-/* These are other names for the standard allocation functions. */
-extern void *__libc_malloc(size_t size);
-extern void *__libc_calloc(size_t num_elems, size_t size);
-extern void *__libc_realloc(void *ptr, size_t size);
-}
-
-static void exit_handler(void) {
- if (failure_enabled && current_malloc_count > malloc_number_to_fail) {
- _exit(88);
- }
-}
-
-static void cpp_new_handler() {
- // Return to try again. It won't fail a second time.
- return;
-}
-
-/* should_fail_allocation returns true if the current allocation should fail. */
-static int should_fail_allocation() {
- static int init = 0;
- char should_fail;
-
- if (in_call) {
- return 0;
- }
-
- in_call = 1;
-
- if (!init) {
- const char *env = getenv("MALLOC_NUMBER_TO_FAIL");
- if (env != NULL && env[0] != 0) {
- char *endptr;
- malloc_number_to_fail = strtoull(env, &endptr, 10);
- if (*endptr == 0) {
- failure_enabled = 1;
- atexit(exit_handler);
- std::set_new_handler(cpp_new_handler);
- }
- }
- abort_on_fail = (NULL != getenv("MALLOC_ABORT_ON_FAIL"));
- init = 1;
- }
-
- in_call = 0;
-
- if (!failure_enabled) {
- return 0;
- }
-
- should_fail = (current_malloc_count == malloc_number_to_fail);
- current_malloc_count++;
-
- if (should_fail && abort_on_fail) {
- abort();
- }
- return should_fail;
-}
-
-extern "C" {
-
-void *malloc(size_t size) {
- if (should_fail_allocation()) {
- return NULL;
- }
-
- return __libc_malloc(size);
-}
-
-void *calloc(size_t num_elems, size_t size) {
- if (should_fail_allocation()) {
- return NULL;
- }
-
- return __libc_calloc(num_elems, size);
-}
-
-void *realloc(void *ptr, size_t size) {
- if (should_fail_allocation()) {
- return NULL;
- }
-
- return __libc_realloc(ptr, size);
-}
-
-} // extern "C"
-
-#endif /* defined(linux) && !ARM && !AARCH64 && !ASAN */
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 4ac7250..edebba1 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -188,6 +188,7 @@ type ConnectionState struct {
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
ChannelID *ecdsa.PublicKey // the channel ID for this connection
SRTPProtectionProfile uint16 // the negotiated DTLS-SRTP protection profile
+ TLSUnique []byte
}
// ClientAuthType declares the policy the server will follow for
@@ -478,7 +479,9 @@ type ProtocolBugs struct {
// MaxHandshakeRecordLength, if non-zero, is the maximum size of a
// handshake record. Handshake messages will be split into multiple
// records at the specified size, except that the client_version will
- // never be fragmented.
+ // never be fragmented. For DTLS, it is the maximum handshake fragment
+ // size, not record size; DTLS allows multiple handshake fragments in a
+ // single handshake record. See |PackHandshakeFragments|.
MaxHandshakeRecordLength int
// FragmentClientVersion will allow MaxHandshakeRecordLength to apply to
@@ -681,13 +684,14 @@ type ProtocolBugs struct {
// fragments in DTLS.
SendEmptyFragments bool
- // NeverResumeOnRenego, if true, causes renegotiations to always be full
- // handshakes.
- NeverResumeOnRenego bool
+ // SendSplitAlert, if true, causes an alert to be sent with the header
+ // and record body split across multiple packets. The peer should
+ // discard these packets rather than process it.
+ SendSplitAlert bool
- // NoSignatureAlgorithmsOnRenego, if true, causes renegotiations to omit
- // the signature_algorithms extension.
- NoSignatureAlgorithmsOnRenego bool
+ // FailIfResumeOnRenego, if true, causes renegotiations to fail if the
+ // client offers a resumption or the server accepts one.
+ FailIfResumeOnRenego bool
// IgnorePeerCipherPreferences, if true, causes the peer's cipher
// preferences to be ignored.
@@ -707,6 +711,22 @@ type ProtocolBugs struct {
// BadFinished, if true, causes the Finished hash to be broken.
BadFinished bool
+
+ // DHGroupPrime, if not nil, is used to define the (finite field)
+ // Diffie-Hellman group. The generator used is always two.
+ DHGroupPrime *big.Int
+
+ // PackHandshakeFragments, if true, causes handshake fragments to be
+ // packed into individual handshake records, up to the specified record
+ // size.
+ PackHandshakeFragments int
+
+ // PackHandshakeRecords, if true, causes handshake records to be packed
+ // into individual packets, up to the specified packet size.
+ PackHandshakeRecords int
+
+ // EnableAllCiphersInDTLS, if true, causes RC4 to be enabled in DTLS.
+ EnableAllCiphersInDTLS bool
}
func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index fd198ca..adbc1c3 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -44,7 +44,11 @@ type Conn struct {
// opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate
// serverName contains the server name indicated by the client, if any.
- serverName string
+ serverName string
+ // firstFinished contains the first Finished hash sent during the
+ // handshake. This is the "tls-unique" channel binding value.
+ firstFinished [12]byte
+
clientRandom, serverRandom [32]byte
masterSecret [48]byte
@@ -1260,6 +1264,15 @@ func (c *Conn) Handshake() error {
return nil
}
+ if c.isDTLS && c.config.Bugs.SendSplitAlert {
+ c.conn.Write([]byte{
+ byte(recordTypeAlert), // type
+ 0xfe, 0xff, // version
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // sequence
+ 0x0, 0x2, // length
+ })
+ c.conn.Write([]byte{alertLevelError, byte(alertInternalError)})
+ }
if c.isClient {
c.handshakeErr = c.clientHandshake()
} else {
@@ -1290,6 +1303,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.ServerName = c.serverName
state.ChannelID = c.channelID
state.SRTPProtectionProfile = c.srtpProtectionProfile
+ state.TLSUnique = c.firstFinished[:]
}
return state
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index 85c4247..50f7786 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -196,6 +196,8 @@ func (c *Conn) dtlsFlushHandshake() error {
return nil
}
+ // This is a test-only DTLS implementation, so there is no need to
+ // retain |c.pendingFragments| for a future retransmit.
var fragments [][]byte
fragments, c.pendingFragments = c.pendingFragments, fragments
@@ -208,38 +210,66 @@ func (c *Conn) dtlsFlushHandshake() error {
fragments = tmp
}
- // Send them all.
+ maxRecordLen := c.config.Bugs.PackHandshakeFragments
+ maxPacketLen := c.config.Bugs.PackHandshakeRecords
+
+ // Pack handshake fragments into records.
+ var records [][]byte
for _, fragment := range fragments {
if c.config.Bugs.SplitFragmentHeader {
- if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:2]); err != nil {
- return err
- }
- fragment = fragment[2:]
- } else if c.config.Bugs.SplitFragmentBody && len(fragment) > 12 {
- if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:13]); err != nil {
- return err
+ records = append(records, fragment[:2])
+ records = append(records, fragment[2:])
+ } else if c.config.Bugs.SplitFragmentBody {
+ if len(fragment) > 12 {
+ records = append(records, fragment[:13])
+ records = append(records, fragment[13:])
+ } else {
+ records = append(records, fragment)
}
- fragment = fragment[13:]
+ } else if i := len(records) - 1; len(records) > 0 && len(records[i])+len(fragment) <= maxRecordLen {
+ records[i] = append(records[i], fragment...)
+ } else {
+ // The fragment will be appended to, so copy it.
+ records = append(records, append([]byte{}, fragment...))
+ }
+ }
+
+ // Format them into packets.
+ var packets [][]byte
+ for _, record := range records {
+ b, err := c.dtlsSealRecord(recordTypeHandshake, record)
+ if err != nil {
+ return err
+ }
+
+ if i := len(packets) - 1; len(packets) > 0 && len(packets[i])+len(b.data) <= maxPacketLen {
+ packets[i] = append(packets[i], b.data...)
+ } else {
+ // The sealed record will be appended to and reused by
+ // |c.out|, so copy it.
+ packets = append(packets, append([]byte{}, b.data...))
}
+ c.out.freeBlock(b)
+ }
- // TODO(davidben): A real DTLS implementation needs to
- // retransmit handshake messages. For testing purposes, we don't
- // actually care.
- if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment); err != nil {
+ // Send all the packets.
+ for _, packet := range packets {
+ if _, err := c.conn.Write(packet); err != nil {
return err
}
}
return nil
}
-func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) {
+// dtlsSealRecord seals a record into a block from |c.out|'s pool.
+func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error) {
recordHeaderLen := dtlsRecordHeaderLen
maxLen := c.config.Bugs.MaxHandshakeRecordLength
if maxLen <= 0 {
maxLen = 1024
}
- b := c.out.newBlock()
+ b = c.out.newBlock()
explicitIVLen := 0
explicitIVIsSeq := false
@@ -286,6 +316,14 @@ func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error
}
copy(b.data[recordHeaderLen+explicitIVLen:], data)
c.out.encrypt(b, explicitIVLen)
+ return
+}
+
+func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) {
+ b, err := c.dtlsSealRecord(typ, data)
+ if err != nil {
+ return
+ }
_, err = c.conn.Write(b.data)
if err != nil {
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 0dac05d..a950313 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -115,7 +115,7 @@ NextCipherSuite:
continue
}
// Don't advertise non-DTLS cipher suites on DTLS.
- if c.isDTLS && suite.flags&suiteNoDTLS != 0 {
+ if c.isDTLS && suite.flags&suiteNoDTLS != 0 && !c.config.Bugs.EnableAllCiphersInDTLS {
continue
}
hello.cipherSuites = append(hello.cipherSuites, suiteId)
@@ -133,16 +133,13 @@ NextCipherSuite:
return errors.New("tls: short read from Rand: " + err.Error())
}
- if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes && (c.cipherSuite == nil || !c.config.Bugs.NoSignatureAlgorithmsOnRenego) {
+ if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes {
hello.signatureAndHashes = c.config.signatureAndHashesForClient()
}
var session *ClientSessionState
var cacheKey string
sessionCache := c.config.ClientSessionCache
- if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil {
- sessionCache = nil
- }
if sessionCache != nil {
hello.ticketSupported = !c.config.SessionTicketsDisabled
@@ -316,10 +313,10 @@ NextCipherSuite:
if err := hs.readSessionTicket(); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(c.firstFinished[:]); err != nil {
return err
}
- if err := hs.sendFinished(isResume); err != nil {
+ if err := hs.sendFinished(nil, isResume); err != nil {
return err
}
} else {
@@ -329,7 +326,7 @@ NextCipherSuite:
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.sendFinished(isResume); err != nil {
+ if err := hs.sendFinished(c.firstFinished[:], isResume); err != nil {
return err
}
// Most retransmits are triggered by a timeout, but the final
@@ -344,7 +341,7 @@ NextCipherSuite:
if err := hs.readSessionTicket(); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(nil); err != nil {
return err
}
}
@@ -727,6 +724,12 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
}
if hs.serverResumedSession() {
+ // For test purposes, assert that the server never accepts the
+ // resumption offer on renegotiation.
+ if c.cipherSuite != nil && c.config.Bugs.FailIfResumeOnRenego {
+ return false, errors.New("tls: server resumed session on renegotiation")
+ }
+
// Restore masterSecret and peerCerts from previous state
hs.masterSecret = hs.session.masterSecret
c.peerCertificates = hs.session.serverCertificates
@@ -737,7 +740,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
return false, nil
}
-func (hs *clientHandshakeState) readFinished() error {
+func (hs *clientHandshakeState) readFinished(out []byte) error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec)
@@ -764,6 +767,7 @@ func (hs *clientHandshakeState) readFinished() error {
}
}
c.serverVerify = append(c.serverVerify[:0], serverFinished.verifyData...)
+ copy(out, serverFinished.verifyData)
hs.writeServerHash(serverFinished.marshal())
return nil
}
@@ -807,7 +811,7 @@ func (hs *clientHandshakeState) readSessionTicket() error {
return nil
}
-func (hs *clientHandshakeState) sendFinished(isResume bool) error {
+func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error {
c := hs.c
var postCCSBytes []byte
@@ -859,6 +863,7 @@ func (hs *clientHandshakeState) sendFinished(isResume bool) error {
} else {
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
}
+ copy(out, finished.verifyData)
if c.config.Bugs.BadFinished {
finished.verifyData[0]++
}
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 59ed9df..85cc0d2 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -69,7 +69,7 @@ func (c *Conn) serverHandshake() error {
return err
}
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(c.firstFinished[:]); err != nil {
return err
}
// Most retransmits are triggered by a timeout, but the final
@@ -81,7 +81,7 @@ func (c *Conn) serverHandshake() error {
}); err != nil {
return err
}
- if err := hs.readFinished(isResume); err != nil {
+ if err := hs.readFinished(nil, isResume); err != nil {
return err
}
c.didResume = true
@@ -94,7 +94,7 @@ func (c *Conn) serverHandshake() error {
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.readFinished(isResume); err != nil {
+ if err := hs.readFinished(c.firstFinished[:], isResume); err != nil {
return err
}
if c.config.Bugs.AlertBeforeFalseStartTest != 0 {
@@ -108,7 +108,7 @@ func (c *Conn) serverHandshake() error {
if err := hs.sendSessionTicket(); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(nil); err != nil {
return err
}
}
@@ -274,6 +274,10 @@ Curves:
hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation
}
+ if c.config.Bugs.NoRenegotiationInfo {
+ hs.hello.secureRenegotiation = nil
+ }
+
hs.hello.compressionMethod = compressionNone
hs.hello.duplicateExtension = c.config.Bugs.DuplicateExtension
if len(hs.clientHello.serverName) > 0 {
@@ -333,6 +337,12 @@ Curves:
_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
+ // For test purposes, check that the peer never offers a session when
+ // renegotiating.
+ if c.cipherSuite != nil && len(hs.clientHello.sessionId) > 0 && c.config.Bugs.FailIfResumeOnRenego {
+ return false, errors.New("tls: offered resumption on renegotiation")
+ }
+
if hs.checkForResumption() {
return true, nil
}
@@ -382,10 +392,6 @@ Curves:
func (hs *serverHandshakeState) checkForResumption() bool {
c := hs.c
- if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil {
- return false
- }
-
if len(hs.clientHello.sessionTicket) > 0 {
if c.config.SessionTicketsDisabled {
return false
@@ -748,7 +754,7 @@ func (hs *serverHandshakeState) establishKeys() error {
return nil
}
-func (hs *serverHandshakeState) readFinished(isResume bool) error {
+func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec)
@@ -817,6 +823,7 @@ func (hs *serverHandshakeState) readFinished(isResume bool) error {
return errors.New("tls: client's Finished message is incorrect")
}
c.clientVerify = append(c.clientVerify[:0], clientFinished.verifyData...)
+ copy(out, clientFinished.verifyData)
hs.writeClientHash(clientFinished.marshal())
return nil
@@ -853,11 +860,12 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
return nil
}
-func (hs *serverHandshakeState) sendFinished() error {
+func (hs *serverHandshakeState) sendFinished(out []byte) error {
c := hs.c
finished := new(finishedMsg)
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+ copy(out, finished.verifyData)
if c.config.Bugs.BadFinished {
finished.verifyData[0]++
}
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 5e44b54..2ee0087 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -561,11 +561,18 @@ type dheKeyAgreement struct {
}
func (ka *dheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
- // 2048-bit MODP Group with 256-bit Prime Order Subgroup (RFC
- // 5114, Section 2.3)
- ka.p, _ = new(big.Int).SetString("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", 16)
- ka.g, _ = new(big.Int).SetString("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", 16)
- q, _ := new(big.Int).SetString("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", 16)
+ var q *big.Int
+ if p := config.Bugs.DHGroupPrime; p != nil {
+ ka.p = p
+ ka.g = big.NewInt(2)
+ q = p
+ } else {
+ // 2048-bit MODP Group with 256-bit Prime Order Subgroup (RFC
+ // 5114, Section 2.3)
+ ka.p, _ = new(big.Int).SetString("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", 16)
+ ka.g, _ = new(big.Int).SetString("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", 16)
+ q, _ = new(big.Int).SetString("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", 16)
+ }
var err error
ka.xOurs, err = rand.Int(config.rand(), q)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index ec2fede..bd03cb1 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "math/big"
"net"
"os"
"os/exec"
@@ -160,6 +161,10 @@ type testCase struct {
// resumeSession controls whether a second connection should be tested
// which attempts to resume the first session.
resumeSession bool
+ // expectResumeRejected, if true, specifies that the attempted
+ // resumption must be rejected by the client. This is only valid for a
+ // serverTest.
+ expectResumeRejected bool
// resumeConfig, if not nil, points to a Config to be used on
// resumption. Unless newSessionsOnResume is set,
// SessionTicketKey, ServerSessionCache, and
@@ -196,6 +201,9 @@ type testCase struct {
// flags, if not empty, contains a list of command-line flags that will
// be passed to the shim program.
flags []string
+ // testTLSUnique, if true, causes the shim to send the tls-unique value
+ // which will be compared against the expected value.
+ testTLSUnique bool
}
var testCases = []testCase{
@@ -1085,6 +1093,49 @@ var testCases = []testCase{
},
expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
},
+ {
+ protocol: dtls,
+ name: "SendSplitAlert-Sync",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSplitAlert: true,
+ },
+ },
+ },
+ {
+ protocol: dtls,
+ name: "SendSplitAlert-Async",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSplitAlert: true,
+ },
+ },
+ flags: []string{"-async"},
+ },
+ {
+ protocol: dtls,
+ name: "PackDTLSHandshake",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: 2,
+ PackHandshakeFragments: 20,
+ PackHandshakeRecords: 200,
+ },
+ },
+ },
+ {
+ testType: serverTest,
+ protocol: dtls,
+ name: "NoRC4-DTLS",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+ Bugs: ProtocolBugs{
+ EnableAllCiphersInDTLS: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":NO_SHARED_CIPHER:",
+ },
}
func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, isResume bool) error {
@@ -1144,16 +1195,20 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
if isResume && test.expectedResumeVersion != 0 {
expectedVersion = test.expectedResumeVersion
}
- if vers := tlsConn.ConnectionState().Version; expectedVersion != 0 && vers != expectedVersion {
+ connState := tlsConn.ConnectionState()
+ if vers := connState.Version; expectedVersion != 0 && vers != expectedVersion {
return fmt.Errorf("got version %x, expected %x", vers, expectedVersion)
}
- if cipher := tlsConn.ConnectionState().CipherSuite; test.expectedCipher != 0 && cipher != test.expectedCipher {
+ if cipher := connState.CipherSuite; test.expectedCipher != 0 && cipher != test.expectedCipher {
return fmt.Errorf("got cipher %x, expected %x", cipher, test.expectedCipher)
}
+ if didResume := connState.DidResume; isResume && didResume == test.expectResumeRejected {
+ return fmt.Errorf("didResume is %t, but we expected the opposite", didResume)
+ }
if test.expectChannelID {
- channelID := tlsConn.ConnectionState().ChannelID
+ channelID := connState.ChannelID
if channelID == nil {
return fmt.Errorf("no channel ID negotiated")
}
@@ -1165,18 +1220,18 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
}
if expected := test.expectedNextProto; expected != "" {
- if actual := tlsConn.ConnectionState().NegotiatedProtocol; actual != expected {
+ if actual := connState.NegotiatedProtocol; actual != expected {
return fmt.Errorf("next proto mismatch: got %s, wanted %s", actual, expected)
}
}
if test.expectedNextProtoType != 0 {
- if (test.expectedNextProtoType == alpn) != tlsConn.ConnectionState().NegotiatedProtocolFromALPN {
+ if (test.expectedNextProtoType == alpn) != connState.NegotiatedProtocolFromALPN {
return fmt.Errorf("next proto type mismatch")
}
}
- if p := tlsConn.ConnectionState().SRTPProtectionProfile; p != test.expectedSRTPProtectionProfile {
+ if p := connState.SRTPProtectionProfile; p != test.expectedSRTPProtectionProfile {
return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile)
}
@@ -1194,6 +1249,17 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
}
}
+ if test.testTLSUnique {
+ var peersValue [12]byte
+ if _, err := io.ReadFull(tlsConn, peersValue[:]); err != nil {
+ return err
+ }
+ expected := tlsConn.ConnectionState().TLSUnique
+ if !bytes.Equal(peersValue[:], expected) {
+ return fmt.Errorf("tls-unique mismatch: peer sent %x, but %x was expected", peersValue[:], expected)
+ }
+ }
+
if test.shimWritesFirst {
var buf [5]byte
_, err := io.ReadFull(tlsConn, buf[:])
@@ -1321,6 +1387,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
panic("Error expected without shouldFail in " + test.name)
}
+ if test.expectResumeRejected && !test.resumeSession {
+ panic("expectResumeRejected without resumeSession in " + test.name)
+ }
+
listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
if err != nil {
panic(err)
@@ -1371,6 +1441,13 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
flags = append(flags, "-use-export-context")
}
}
+ if test.expectResumeRejected {
+ flags = append(flags, "-expect-session-miss")
+ }
+
+ if test.testTLSUnique {
+ flags = append(flags, "-tls-unique")
+ }
flags = append(flags, test.flags...)
@@ -1569,6 +1646,14 @@ func isDTLSCipher(suiteName string) bool {
return !hasComponent(suiteName, "RC4")
}
+func bigFromHex(hex string) *big.Int {
+ ret, ok := new(big.Int).SetString(hex, 16)
+ if !ok {
+ panic("failed to parse hex number 0x" + hex)
+ }
+ return ret
+}
+
func addCipherSuiteTests() {
for _, suite := range testCipherSuites {
const psk = "12345"
@@ -1667,6 +1752,21 @@ func addCipherSuiteTests() {
}
}
}
+
+ testCases = append(testCases, testCase{
+ name: "WeakDH",
+ config: Config{
+ CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ // This is a 1023-bit prime number, generated
+ // with:
+ // openssl gendh 1023 | openssl asn1parse -i
+ DHGroupPrime: bigFromHex("518E9B7930CE61C6E445C8360584E5FC78D9137C0FFDC880B495D5338ADF7689951A6821C17A76B3ACB8E0156AEA607B7EC406EBEDBB84D8376EB8FE8F8BA1433488BEE0C3EDDFD3A32DBB9481980A7AF6C96BFCF490A094CFFB2B8192C1BB5510B77B658436E27C2D4D023FE3718222AB0CA1273995B51F6D625A4944D0DD4B"),
+ },
+ },
+ shouldFail: true,
+ expectedError: "BAD_DH_P_LENGTH",
+ })
}
func addBadECDSASignatureTests() {
@@ -1866,245 +1966,235 @@ func addExtendedMasterSecretTests() {
}
}
- // When a session is resumed, it should still be aware that its master
- // secret was generated via EMS and thus it's safe to use tls-unique.
- testCases = append(testCases, testCase{
- name: "ExtendedMasterSecret-Resume",
- config: Config{
- Bugs: ProtocolBugs{
- RequireExtendedMasterSecret: true,
- },
- },
- flags: []string{expectEMSFlag},
- resumeSession: true,
- })
+ for _, isClient := range []bool{false, true} {
+ for _, supportedInFirstConnection := range []bool{false, true} {
+ for _, supportedInResumeConnection := range []bool{false, true} {
+ boolToWord := func(b bool) string {
+ if b {
+ return "Yes"
+ }
+ return "No"
+ }
+ suffix := boolToWord(supportedInFirstConnection) + "To" + boolToWord(supportedInResumeConnection) + "-"
+ if isClient {
+ suffix += "Client"
+ } else {
+ suffix += "Server"
+ }
+
+ supportedConfig := Config{
+ Bugs: ProtocolBugs{
+ RequireExtendedMasterSecret: true,
+ },
+ }
+
+ noSupportConfig := Config{
+ Bugs: ProtocolBugs{
+ NoExtendedMasterSecret: true,
+ },
+ }
+
+ test := testCase{
+ name: "ExtendedMasterSecret-" + suffix,
+ resumeSession: true,
+ }
+
+ if !isClient {
+ test.testType = serverTest
+ }
+
+ if supportedInFirstConnection {
+ test.config = supportedConfig
+ } else {
+ test.config = noSupportConfig
+ }
+
+ if supportedInResumeConnection {
+ test.resumeConfig = &supportedConfig
+ } else {
+ test.resumeConfig = &noSupportConfig
+ }
+
+ switch suffix {
+ case "YesToYes-Client", "YesToYes-Server":
+ // When a session is resumed, it should
+ // still be aware that its master
+ // secret was generated via EMS and
+ // thus it's safe to use tls-unique.
+ test.flags = []string{expectEMSFlag}
+ case "NoToYes-Server":
+ // If an original connection did not
+ // contain EMS, but a resumption
+ // handshake does, then a server should
+ // not resume the session.
+ test.expectResumeRejected = true
+ case "YesToNo-Server":
+ // Resuming an EMS session without the
+ // EMS extension should cause the
+ // server to abort the connection.
+ test.shouldFail = true
+ test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:"
+ case "NoToYes-Client":
+ // A client should abort a connection
+ // where the server resumed a non-EMS
+ // session but echoed the EMS
+ // extension.
+ test.shouldFail = true
+ test.expectedError = ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:"
+ case "YesToNo-Client":
+ // A client should abort a connection
+ // where the server didn't echo EMS
+ // when the session used it.
+ test.shouldFail = true
+ test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:"
+ }
+
+ testCases = append(testCases, test)
+ }
+ }
+ }
}
// Adds tests that try to cover the range of the handshake state machine, under
// various conditions. Some of these are redundant with other tests, but they
// only cover the synchronous case.
func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) {
- var suffix string
- var flags []string
- var maxHandshakeRecordLength int
- if protocol == dtls {
- suffix = "-DTLS"
- }
- if async {
- suffix += "-Async"
- flags = append(flags, "-async")
- } else {
- suffix += "-Sync"
- }
- if splitHandshake {
- suffix += "-SplitHandshakeRecords"
- maxHandshakeRecordLength = 1
- }
+ var tests []testCase
// Basic handshake, with resumption. Client and server,
// session ID and session ticket.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "Basic-Client" + suffix,
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
- },
- flags: flags,
+ tests = append(tests, testCase{
+ name: "Basic-Client",
resumeSession: true,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "Basic-Client-RenewTicket" + suffix,
+ tests = append(tests, testCase{
+ name: "Basic-Client-RenewTicket",
config: Config{
Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- RenewTicketOnResume: true,
+ RenewTicketOnResume: true,
},
},
- flags: flags,
resumeSession: true,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "Basic-Client-NoTicket" + suffix,
+ tests = append(tests, testCase{
+ name: "Basic-Client-NoTicket",
config: Config{
SessionTicketsDisabled: true,
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: flags,
resumeSession: true,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "Basic-Client-Implicit" + suffix,
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
- },
- flags: append(flags, "-implicit-handshake"),
+ tests = append(tests, testCase{
+ name: "Basic-Client-Implicit",
+ flags: []string{"-implicit-handshake"},
resumeSession: true,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: serverTest,
- name: "Basic-Server" + suffix,
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
- },
- flags: flags,
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "Basic-Server",
resumeSession: true,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: serverTest,
- name: "Basic-Server-NoTickets" + suffix,
+ name: "Basic-Server-NoTickets",
config: Config{
SessionTicketsDisabled: true,
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: flags,
resumeSession: true,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: serverTest,
- name: "Basic-Server-Implicit" + suffix,
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
- },
- flags: append(flags, "-implicit-handshake"),
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "Basic-Server-Implicit",
+ flags: []string{"-implicit-handshake"},
resumeSession: true,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: serverTest,
- name: "Basic-Server-EarlyCallback" + suffix,
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
- },
- flags: append(flags, "-use-early-callback"),
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "Basic-Server-EarlyCallback",
+ flags: []string{"-use-early-callback"},
resumeSession: true,
})
// TLS client auth.
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: clientTest,
- name: "ClientAuth-Client" + suffix,
+ name: "ClientAuth-Client",
config: Config{
ClientAuth: RequireAnyClientCert,
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags,
+ flags: []string{
"-cert-file", rsaCertificateFile,
- "-key-file", rsaKeyFile),
+ "-key-file", rsaKeyFile,
+ },
})
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: serverTest,
- name: "ClientAuth-Server" + suffix,
+ name: "ClientAuth-Server",
config: Config{
Certificates: []Certificate{rsaCertificate},
},
- flags: append(flags, "-require-any-client-certificate"),
+ flags: []string{"-require-any-client-certificate"},
})
// No session ticket support; server doesn't send NewSessionTicket.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "SessionTicketsDisabled-Client" + suffix,
+ tests = append(tests, testCase{
+ name: "SessionTicketsDisabled-Client",
config: Config{
SessionTicketsDisabled: true,
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: flags,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: serverTest,
- name: "SessionTicketsDisabled-Server" + suffix,
+ name: "SessionTicketsDisabled-Server",
config: Config{
SessionTicketsDisabled: true,
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: flags,
})
// Skip ServerKeyExchange in PSK key exchange if there's no
// identity hint.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "EmptyPSKHint-Client" + suffix,
+ tests = append(tests, testCase{
+ name: "EmptyPSKHint-Client",
config: Config{
CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA},
PreSharedKey: []byte("secret"),
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags, "-psk", "secret"),
+ flags: []string{"-psk", "secret"},
})
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: serverTest,
- name: "EmptyPSKHint-Server" + suffix,
+ name: "EmptyPSKHint-Server",
config: Config{
CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA},
PreSharedKey: []byte("secret"),
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags, "-psk", "secret"),
+ flags: []string{"-psk", "secret"},
})
if protocol == tls {
+ tests = append(tests, testCase{
+ name: "Renegotiate-Client",
+ renegotiate: true,
+ })
// NPN on client and server; results in post-handshake message.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "NPN-Client" + suffix,
+ tests = append(tests, testCase{
+ name: "NPN-Client",
config: Config{
NextProtos: []string{"foo"},
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags, "-select-next-proto", "foo"),
+ flags: []string{"-select-next-proto", "foo"},
expectedNextProto: "foo",
expectedNextProtoType: npn,
})
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: serverTest,
- name: "NPN-Server" + suffix,
+ name: "NPN-Server",
config: Config{
NextProtos: []string{"bar"},
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags,
+ flags: []string{
"-advertise-npn", "\x03foo\x03bar\x03baz",
- "-expect-next-proto", "bar"),
+ "-expect-next-proto", "bar",
+ },
expectedNextProto: "bar",
expectedNextProtoType: npn,
})
@@ -2112,146 +2202,148 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
// TODO(davidben): Add tests for when False Start doesn't trigger.
// Client does False Start and negotiates NPN.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "FalseStart" + suffix,
+ tests = append(tests, testCase{
+ name: "FalseStart",
config: Config{
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
NextProtos: []string{"foo"},
Bugs: ProtocolBugs{
- ExpectFalseStart: true,
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ ExpectFalseStart: true,
},
},
- flags: append(flags,
+ flags: []string{
"-false-start",
- "-select-next-proto", "foo"),
+ "-select-next-proto", "foo",
+ },
shimWritesFirst: true,
resumeSession: true,
})
// Client does False Start and negotiates ALPN.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "FalseStart-ALPN" + suffix,
+ tests = append(tests, testCase{
+ name: "FalseStart-ALPN",
config: Config{
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
NextProtos: []string{"foo"},
Bugs: ProtocolBugs{
- ExpectFalseStart: true,
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ ExpectFalseStart: true,
},
},
- flags: append(flags,
+ flags: []string{
"-false-start",
- "-advertise-alpn", "\x03foo"),
+ "-advertise-alpn", "\x03foo",
+ },
shimWritesFirst: true,
resumeSession: true,
})
// Client does False Start but doesn't explicitly call
// SSL_connect.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "FalseStart-Implicit" + suffix,
+ tests = append(tests, testCase{
+ name: "FalseStart-Implicit",
config: Config{
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
NextProtos: []string{"foo"},
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags,
+ flags: []string{
"-implicit-handshake",
"-false-start",
- "-advertise-alpn", "\x03foo"),
+ "-advertise-alpn", "\x03foo",
+ },
})
// False Start without session tickets.
- testCases = append(testCases, testCase{
- name: "FalseStart-SessionTicketsDisabled" + suffix,
+ tests = append(tests, testCase{
+ name: "FalseStart-SessionTicketsDisabled",
config: Config{
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
NextProtos: []string{"foo"},
SessionTicketsDisabled: true,
Bugs: ProtocolBugs{
- ExpectFalseStart: true,
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ ExpectFalseStart: true,
},
},
- flags: append(flags,
+ flags: []string{
"-false-start",
"-select-next-proto", "foo",
- ),
+ },
shimWritesFirst: true,
})
// Server parses a V2ClientHello.
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: serverTest,
- name: "SendV2ClientHello" + suffix,
+ name: "SendV2ClientHello",
config: Config{
// Choose a cipher suite that does not involve
// elliptic curves, so no extensions are
// involved.
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- SendV2ClientHello: true,
+ SendV2ClientHello: true,
},
},
- flags: flags,
})
// Client sends a Channel ID.
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "ChannelID-Client" + suffix,
+ tests = append(tests, testCase{
+ name: "ChannelID-Client",
config: Config{
RequestChannelID: true,
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags,
- "-send-channel-id", channelIDKeyFile,
- ),
+ flags: []string{"-send-channel-id", channelIDKeyFile},
resumeSession: true,
expectChannelID: true,
})
// Server accepts a Channel ID.
- testCases = append(testCases, testCase{
- protocol: protocol,
+ tests = append(tests, testCase{
testType: serverTest,
- name: "ChannelID-Server" + suffix,
+ name: "ChannelID-Server",
config: Config{
ChannelID: channelIDKey,
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
},
- flags: append(flags,
+ flags: []string{
"-expect-channel-id",
base64.StdEncoding.EncodeToString(channelIDBytes),
- ),
+ },
resumeSession: true,
expectChannelID: true,
})
} else {
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "SkipHelloVerifyRequest" + suffix,
+ tests = append(tests, testCase{
+ name: "SkipHelloVerifyRequest",
config: Config{
Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- SkipHelloVerifyRequest: true,
+ SkipHelloVerifyRequest: true,
},
},
- flags: flags,
})
}
+
+ var suffix string
+ var flags []string
+ var maxHandshakeRecordLength int
+ if protocol == dtls {
+ suffix = "-DTLS"
+ }
+ if async {
+ suffix += "-Async"
+ flags = append(flags, "-async")
+ } else {
+ suffix += "-Sync"
+ }
+ if splitHandshake {
+ suffix += "-SplitHandshakeRecords"
+ maxHandshakeRecordLength = 1
+ }
+ for _, test := range tests {
+ test.protocol = protocol
+ test.name += suffix
+ test.config.Bugs.MaxHandshakeRecordLength = maxHandshakeRecordLength
+ test.flags = append(test.flags, flags...)
+ testCases = append(testCases, test)
+ }
}
func addDDoSCallbackTests() {
@@ -2637,8 +2729,8 @@ func addExtensionTests() {
CorruptTicket: true,
},
},
- resumeSession: true,
- flags: []string{"-expect-session-miss"},
+ resumeSession: true,
+ expectResumeRejected: true,
})
// Resume with an oversized session id.
testCases = append(testCases, testCase{
@@ -2799,7 +2891,6 @@ func addResumptionVersionTests() {
testCases = append(testCases, testCase{
protocol: protocol,
name: "Resume-Client-NoResume" + suffix,
- flags: []string{"-expect-session-miss"},
resumeSession: true,
config: Config{
MaxVersion: sessionVers.version,
@@ -2811,24 +2902,21 @@ func addResumptionVersionTests() {
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
},
newSessionsOnResume: true,
+ expectResumeRejected: true,
expectedResumeVersion: resumeVers.version,
})
- var flags []string
- if sessionVers.version != resumeVers.version {
- flags = append(flags, "-expect-session-miss")
- }
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "Resume-Server" + suffix,
- flags: flags,
resumeSession: true,
config: Config{
MaxVersion: sessionVers.version,
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
},
- expectedVersion: sessionVers.version,
+ expectedVersion: sessionVers.version,
+ expectResumeRejected: sessionVers.version != resumeVers.version,
resumeConfig: &Config{
MaxVersion: resumeVers.version,
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
@@ -2857,57 +2945,50 @@ func addResumptionVersionTests() {
}
func addRenegotiationTests() {
+ // Servers cannot renegotiate.
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server",
- flags: []string{"-renegotiate"},
- shimWritesFirst: true,
+ testType: serverTest,
+ name: "Renegotiate-Server-Forbidden",
+ renegotiate: true,
+ flags: []string{"-reject-peer-renegotiations"},
+ shouldFail: true,
+ expectedError: ":NO_RENEGOTIATION:",
+ expectedLocalError: "remote error: no renegotiation",
})
+ // TODO(agl): test the renegotiation info SCSV.
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-Full",
+ name: "Renegotiate-Client",
config: Config{
Bugs: ProtocolBugs{
- NeverResumeOnRenego: true,
+ FailIfResumeOnRenego: true,
},
},
- flags: []string{"-renegotiate"},
- shimWritesFirst: true,
+ renegotiate: true,
})
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-EmptyExt",
+ name: "Renegotiate-Client-EmptyExt",
+ renegotiate: true,
config: Config{
Bugs: ProtocolBugs{
EmptyRenegotiationInfo: true,
},
},
- flags: []string{"-renegotiate"},
- shimWritesFirst: true,
- shouldFail: true,
- expectedError: ":RENEGOTIATION_MISMATCH:",
+ shouldFail: true,
+ expectedError: ":RENEGOTIATION_MISMATCH:",
})
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-BadExt",
+ name: "Renegotiate-Client-BadExt",
+ renegotiate: true,
config: Config{
Bugs: ProtocolBugs{
BadRenegotiationInfo: true,
},
},
- flags: []string{"-renegotiate"},
- shimWritesFirst: true,
- shouldFail: true,
- expectedError: ":RENEGOTIATION_MISMATCH:",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-ClientInitiated",
- renegotiate: true,
+ shouldFail: true,
+ expectedError: ":RENEGOTIATION_MISMATCH:",
})
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-ClientInitiated-NoExt",
+ name: "Renegotiate-Client-NoExt",
renegotiate: true,
config: Config{
Bugs: ProtocolBugs{
@@ -2916,75 +2997,16 @@ func addRenegotiationTests() {
},
shouldFail: true,
expectedError: ":UNSAFE_LEGACY_RENEGOTIATION_DISABLED:",
+ flags: []string{"-no-legacy-server-connect"},
})
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-ClientInitiated-NoExt-Allowed",
+ name: "Renegotiate-Client-NoExt-Allowed",
renegotiate: true,
config: Config{
Bugs: ProtocolBugs{
NoRenegotiationInfo: true,
},
},
- flags: []string{"-allow-unsafe-legacy-renegotiation"},
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-ClientInitiated-Forbidden",
- renegotiate: true,
- flags: []string{"-reject-peer-renegotiations"},
- shouldFail: true,
- expectedError: ":NO_RENEGOTIATION:",
- expectedLocalError: "remote error: no renegotiation",
- })
- // Regression test for CVE-2015-0291.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Renegotiate-Server-NoSignatureAlgorithms",
- config: Config{
- Bugs: ProtocolBugs{
- NeverResumeOnRenego: true,
- NoSignatureAlgorithmsOnRenego: true,
- },
- },
- flags: []string{"-renegotiate"},
- shimWritesFirst: true,
- })
- // TODO(agl): test the renegotiation info SCSV.
- testCases = append(testCases, testCase{
- name: "Renegotiate-Client",
- renegotiate: true,
- })
- testCases = append(testCases, testCase{
- name: "Renegotiate-Client-Full",
- config: Config{
- Bugs: ProtocolBugs{
- NeverResumeOnRenego: true,
- },
- },
- renegotiate: true,
- })
- testCases = append(testCases, testCase{
- name: "Renegotiate-Client-EmptyExt",
- renegotiate: true,
- config: Config{
- Bugs: ProtocolBugs{
- EmptyRenegotiationInfo: true,
- },
- },
- shouldFail: true,
- expectedError: ":RENEGOTIATION_MISMATCH:",
- })
- testCases = append(testCases, testCase{
- name: "Renegotiate-Client-BadExt",
- renegotiate: true,
- config: Config{
- Bugs: ProtocolBugs{
- BadRenegotiationInfo: true,
- },
- },
- shouldFail: true,
- expectedError: ":RENEGOTIATION_MISMATCH:",
})
testCases = append(testCases, testCase{
name: "Renegotiate-Client-SwitchCiphers",
@@ -3365,6 +3387,59 @@ func addExportKeyingMaterialTests() {
})
}
+func addTLSUniqueTests() {
+ for _, isClient := range []bool{false, true} {
+ for _, isResumption := range []bool{false, true} {
+ for _, hasEMS := range []bool{false, true} {
+ var suffix string
+ if isResumption {
+ suffix = "Resume-"
+ } else {
+ suffix = "Full-"
+ }
+
+ if hasEMS {
+ suffix += "EMS-"
+ } else {
+ suffix += "NoEMS-"
+ }
+
+ if isClient {
+ suffix += "Client"
+ } else {
+ suffix += "Server"
+ }
+
+ test := testCase{
+ name: "TLSUnique-" + suffix,
+ testTLSUnique: true,
+ config: Config{
+ Bugs: ProtocolBugs{
+ NoExtendedMasterSecret: !hasEMS,
+ },
+ },
+ }
+
+ if isResumption {
+ test.resumeSession = true
+ test.resumeConfig = &Config{
+ Bugs: ProtocolBugs{
+ NoExtendedMasterSecret: !hasEMS,
+ },
+ }
+ }
+
+ if isResumption && !hasEMS {
+ test.shouldFail = true
+ test.expectedError = "failed to get tls-unique"
+ }
+
+ testCases = append(testCases, test)
+ }
+ }
+ }
+}
+
func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
defer wg.Done()
@@ -3463,6 +3538,7 @@ func main() {
addFastRadioPaddingTests()
addDTLSRetransmitTests()
addExportKeyingMaterialTests()
+ addTLSUniqueTests()
for _, async := range []bool{false, true} {
for _, splitHandshake := range []bool{false, true} {
for _, protocol := range []protocol{tls, dtls} {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 25906f7..363b6f3 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -65,7 +65,6 @@ const Flag<bool> kBoolFlags[] = {
{ "-expect-session-miss", &TestConfig::expect_session_miss },
{ "-expect-extended-master-secret",
&TestConfig::expect_extended_master_secret },
- { "-renegotiate", &TestConfig::renegotiate },
{ "-allow-unsafe-legacy-renegotiation",
&TestConfig::allow_unsafe_legacy_renegotiation },
{ "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling },
@@ -81,6 +80,8 @@ const Flag<bool> kBoolFlags[] = {
{ "-handshake-never-done", &TestConfig::handshake_never_done },
{ "-use-export-context", &TestConfig::use_export_context },
{ "-reject-peer-renegotiations", &TestConfig::reject_peer_renegotiations },
+ { "-no-legacy-server-connect", &TestConfig::no_legacy_server_connect },
+ { "-tls-unique", &TestConfig::tls_unique },
};
const Flag<std::string> kStringFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index f107a0f..5d753c8 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -54,7 +54,6 @@ struct TestConfig {
bool expect_extended_master_secret = false;
std::string psk;
std::string psk_identity;
- bool renegotiate = false;
bool allow_unsafe_legacy_renegotiation = false;
std::string srtp_profiles;
bool enable_ocsp_stapling = false;
@@ -78,6 +77,8 @@ struct TestConfig {
std::string export_context;
bool use_export_context = false;
bool reject_peer_renegotiations = false;
+ bool no_legacy_server_connect = false;
+ bool tls_unique = false;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);