From afc64dec7428d7e16d74dedbdb77f35e99166c38 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Tue, 19 Jul 2016 17:12:41 +0200 Subject: [PATCH] Add tests to ensure our ClientHello does not change. We'll need to update it on occasion, but we should not update our default ClientHello without noticing. Change-Id: I19ca52fdbe10e3ac14413fecd16be2e58af5a1f6 Reviewed-on: https://boringssl-review.googlesource.com/9075 Reviewed-by: Steven Valdez Reviewed-by: David Benjamin Commit-Queue: David Benjamin CQ-Verified: CQ bot account: commit-bot@chromium.org --- ssl/ssl_test.cc | 172 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 155 insertions(+), 17 deletions(-) diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 86e79422..0dc0884b 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -796,6 +796,29 @@ static ScopedSSL_SESSION CreateSessionWithTicket(size_t ticket_len) { return session; } +static bool GetClientHello(SSL *ssl, std::vector *out) { + ScopedBIO bio(BIO_new(BIO_s_mem())); + if (!bio) { + return false; + } + // Do not configure a reading BIO, but record what's written to a memory BIO. + SSL_set_bio(ssl, nullptr /* rbio */, BIO_up_ref(bio.get())); + int ret = SSL_connect(ssl); + if (ret > 0) { + // SSL_connect should fail without a BIO to write to. + return false; + } + ERR_clear_error(); + + const uint8_t *client_hello; + size_t client_hello_len; + if (!BIO_mem_contents(bio.get(), &client_hello, &client_hello_len)) { + return false; + } + *out = std::vector(client_hello, client_hello + client_hello_len); + return true; +} + // GetClientHelloLen creates a client SSL connection with a ticket of length // |ticket_len| and records the ClientHello. It returns the length of the // ClientHello, not including the record header, on success and zero on error. @@ -806,26 +829,15 @@ static size_t GetClientHelloLen(size_t ticket_len) { return 0; } ScopedSSL ssl(SSL_new(ctx.get())); - ScopedBIO bio(BIO_new(BIO_s_mem())); - if (!ssl || !bio || !SSL_set_session(ssl.get(), session.get())) { + if (!ssl || !SSL_set_session(ssl.get(), session.get())) { return 0; } - // Do not configure a reading BIO, but record what's written to a memory BIO. - SSL_set_bio(ssl.get(), nullptr /* rbio */, BIO_up_ref(bio.get())); - int ret = SSL_connect(ssl.get()); - if (ret > 0) { - // SSL_connect should fail without a BIO to write to. + std::vector client_hello; + if (!GetClientHello(ssl.get(), &client_hello) || + client_hello.size() <= SSL3_RT_HEADER_LENGTH) { return 0; } - ERR_clear_error(); - - const uint8_t *unused; - size_t client_hello_len; - if (!BIO_mem_contents(bio.get(), &unused, &client_hello_len) || - client_hello_len <= SSL3_RT_HEADER_LENGTH) { - return 0; - } - return client_hello_len - SSL3_RT_HEADER_LENGTH; + return client_hello.size() - SSL3_RT_HEADER_LENGTH; } struct PaddingTest { @@ -1575,6 +1587,131 @@ static bool TestRetainOnlySHA256OfCerts() { return true; } +static bool ClientHelloMatches(uint16_t version, const uint8_t *expected, + size_t expected_len) { + ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method())); + if (!ctx) { + return false; + } + SSL_CTX_set_max_version(ctx.get(), version); + // Our default cipher list varies by CPU capabilities, so manually place the + // ChaCha20 ciphers in front. + if (!SSL_CTX_set_cipher_list(ctx.get(), "CHACHA20:ALL")) { + return false; + } + ScopedSSL ssl(SSL_new(ctx.get())); + if (!ssl) { + return false; + } + std::vector client_hello; + if (!GetClientHello(ssl.get(), &client_hello)) { + return false; + } + + // Zero the client_random. + constexpr size_t kRandomOffset = 1 + 2 + 2 + // record header + 1 + 3 + // handshake message header + 2; // client_version + if (client_hello.size() < kRandomOffset + SSL3_RANDOM_SIZE) { + fprintf(stderr, "ClientHello for version %04x too short.\n", version); + return false; + } + memset(client_hello.data() + kRandomOffset, 0, SSL3_RANDOM_SIZE); + + if (client_hello.size() != expected_len || + memcmp(client_hello.data(), expected, expected_len) != 0) { + fprintf(stderr, "ClientHello for version %04x did not match:\n", version); + fprintf(stderr, "Got:\n\t"); + for (size_t i = 0; i < client_hello.size(); i++) { + fprintf(stderr, "0x%02x, ", client_hello[i]); + } + fprintf(stderr, "\nWanted:\n\t"); + for (size_t i = 0; i < expected_len; i++) { + fprintf(stderr, "0x%02x, ", expected[i]); + } + fprintf(stderr, "\n"); + return false; + } + + return true; +} + +// Tests that our ClientHellos do not change unexpectedly. +static bool TestClientHello() { + static const uint8_t kSSL3ClientHello[] = { + 0x16, 0x03, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x43, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0xc0, 0x0a, 0xc0, + 0x14, 0x00, 0x39, 0xc0, 0x07, 0xc0, 0x11, 0x00, 0x2f, 0x00, 0x35, + 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00, 0xff, 0x01, 0x00, + }; + if (!ClientHelloMatches(SSL3_VERSION, kSSL3ClientHello, + sizeof(kSSL3ClientHello))) { + return false; + } + + static const uint8_t kTLS1ClientHello[] = { + 0x16, 0x03, 0x01, 0x00, 0x66, 0x01, 0x00, 0x00, 0x62, 0x03, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xc0, 0x09, + 0xc0, 0x13, 0x00, 0x33, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x39, 0xc0, 0x07, + 0xc0, 0x11, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + }; + if (!ClientHelloMatches(TLS1_VERSION, kTLS1ClientHello, + sizeof(kTLS1ClientHello))) { + return false; + } + + static const uint8_t kTLS11ClientHello[] = { + 0x16, 0x03, 0x01, 0x00, 0x66, 0x01, 0x00, 0x00, 0x62, 0x03, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xc0, 0x09, + 0xc0, 0x13, 0x00, 0x33, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x39, 0xc0, 0x07, + 0xc0, 0x11, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + }; + if (!ClientHelloMatches(TLS1_1_VERSION, kTLS11ClientHello, + sizeof(kTLS11ClientHello))) { + return false; + } + + static const uint8_t kTLS12ClientHello[] = { + 0x16, 0x03, 0x01, 0x00, 0xa4, 0x01, 0x00, 0x00, 0xa0, 0x03, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xcc, 0xa9, + 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e, + 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xc0, 0x09, 0xc0, 0x23, 0xc0, 0x13, + 0xc0, 0x27, 0x00, 0x33, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x24, 0xc0, 0x14, + 0xc0, 0x28, 0x00, 0x39, 0x00, 0x6b, 0xc0, 0x07, 0xc0, 0x11, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x0a, + 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, 0x00, 0x35, 0xff, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x12, 0x00, 0x10, 0x06, 0x01, 0x06, 0x03, 0x05, 0x01, 0x05, 0x03, 0x04, + 0x01, 0x04, 0x03, 0x02, 0x01, 0x02, 0x03, 0x00, 0x0b, 0x00, 0x02, 0x01, + 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, + 0x18, + }; + if (!ClientHelloMatches(TLS1_2_VERSION, kTLS12ClientHello, + sizeof(kTLS12ClientHello))) { + return false; + } + + // TODO(davidben): Add a change detector for TLS 1.3 once the spec and our + // implementation has settled enough that it won't change. + + return true; +} + int main() { CRYPTO_library_init(); @@ -1605,7 +1742,8 @@ int main() { !TestSetFD() || !TestSetBIO() || !TestGetPeerCertificate() || - !TestRetainOnlySHA256OfCerts()) { + !TestRetainOnlySHA256OfCerts() || + !TestClientHello()) { ERR_print_errors_fp(stderr); return 1; }