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 <svaldez@google.com> Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
parent
3ce4389e96
commit
afc64dec74
172
ssl/ssl_test.cc
172
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<uint8_t> *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<uint8_t>(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<uint8_t> 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<uint8_t> 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user