chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,40 @@
add_executable(
bssl_shim
async_bio.cc
bssl_shim.cc
handshake_util.cc
mock_quic_transport.cc
packeted_bio.cc
settings_writer.cc
ssl_transfer.cc
test_config.cc
test_state.cc
)
target_link_libraries(bssl_shim test_support_lib ssl crypto)
if(WIN32)
target_link_libraries(bssl_shim ws2_32)
endif()
target_add_awslc_include_paths(TARGET bssl_shim SCOPE PRIVATE)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_executable(
handshaker
async_bio.cc
handshake_util.cc
handshaker.cc
mock_quic_transport.cc
packeted_bio.cc
settings_writer.cc
test_config.cc
test_state.cc
)
target_link_libraries(handshaker test_support_lib ssl crypto)
target_add_awslc_include_paths(TARGET handshaker SCOPE PRIVATE)
else()
# Declare a dummy target for run_tests to depend on.
add_custom_target(handshaker)
endif()

View File

@@ -0,0 +1,179 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#include "async_bio.h"
#include <errno.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/mem.h>
#include "../../crypto/internal.h"
namespace {
extern const BIO_METHOD g_async_bio_method;
struct AsyncBio {
bool datagram;
bool enforce_write_quota;
size_t read_quota;
size_t write_quota;
};
AsyncBio *GetData(BIO *bio) {
if (bio->method != &g_async_bio_method) {
return NULL;
}
return (AsyncBio *)bio->ptr;
}
static int AsyncWrite(BIO *bio, const char *in, int inl) {
AsyncBio *a = GetData(bio);
if (a == NULL || bio->next_bio == NULL) {
return 0;
}
if (!a->enforce_write_quota) {
return BIO_write(bio->next_bio, in, inl);
}
BIO_clear_retry_flags(bio);
if (a->write_quota == 0) {
BIO_set_retry_write(bio);
errno = EAGAIN;
return -1;
}
if (!a->datagram && static_cast<size_t>(inl) > a->write_quota) {
inl = static_cast<int>(a->write_quota);
}
int ret = BIO_write(bio->next_bio, in, inl);
if (ret <= 0) {
BIO_copy_next_retry(bio);
} else {
a->write_quota -= (a->datagram ? 1 : ret);
}
return ret;
}
static int AsyncRead(BIO *bio, char *out, int outl) {
AsyncBio *a = GetData(bio);
if (a == NULL || bio->next_bio == NULL) {
return 0;
}
BIO_clear_retry_flags(bio);
if (a->read_quota == 0) {
BIO_set_retry_read(bio);
errno = EAGAIN;
return -1;
}
if (!a->datagram && static_cast<size_t>(outl) > a->read_quota) {
outl = static_cast<int>(a->read_quota);
}
int ret = BIO_read(bio->next_bio, out, outl);
if (ret <= 0) {
BIO_copy_next_retry(bio);
} else {
a->read_quota -= (a->datagram ? 1 : ret);
}
return ret;
}
static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
if (bio->next_bio == NULL) {
return 0;
}
BIO_clear_retry_flags(bio);
long ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
BIO_copy_next_retry(bio);
return ret;
}
static int AsyncNew(BIO *bio) {
AsyncBio *a = (AsyncBio *)OPENSSL_zalloc(sizeof(*a));
if (a == NULL) {
return 0;
}
a->enforce_write_quota = true;
bio->init = 1;
bio->ptr = (char *)a;
return 1;
}
static int AsyncFree(BIO *bio) {
if (bio == NULL) {
return 0;
}
OPENSSL_free(bio->ptr);
bio->ptr = NULL;
bio->init = 0;
bio->flags = 0;
return 1;
}
static long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
if (bio->next_bio == NULL) {
return 0;
}
return BIO_callback_ctrl(bio->next_bio, cmd, fp);
}
const BIO_METHOD g_async_bio_method = {
BIO_TYPE_FILTER,
"async bio",
AsyncWrite,
AsyncRead,
NULL /* puts */,
NULL /* gets */,
AsyncCtrl,
AsyncNew,
AsyncFree,
AsyncCallbackCtrl,
};
} // namespace
bssl::UniquePtr<BIO> AsyncBioCreate() {
return bssl::UniquePtr<BIO>(BIO_new(&g_async_bio_method));
}
bssl::UniquePtr<BIO> AsyncBioCreateDatagram() {
bssl::UniquePtr<BIO> ret(BIO_new(&g_async_bio_method));
if (!ret) {
return nullptr;
}
GetData(ret.get())->datagram = true;
return ret;
}
void AsyncBioAllowRead(BIO *bio, size_t count) {
AsyncBio *a = GetData(bio);
if (a == NULL) {
return;
}
a->read_quota += count;
}
void AsyncBioAllowWrite(BIO *bio, size_t count) {
AsyncBio *a = GetData(bio);
if (a == NULL) {
return;
}
a->write_quota += count;
}
void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) {
AsyncBio *a = GetData(bio);
if (a == NULL) {
return;
}
a->enforce_write_quota = enforce;
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_ASYNC_BIO
#define HEADER_ASYNC_BIO
#include <openssl/bio.h>
// AsyncBioCreate creates a filter BIO for testing asynchronous state
// machines which consume a stream socket. Reads and writes will fail
// and return EAGAIN unless explicitly allowed. Each async BIO has a
// read quota and a write quota. Initially both are zero. As each is
// incremented, bytes are allowed to flow through the BIO.
bssl::UniquePtr<BIO> AsyncBioCreate();
// AsyncBioCreateDatagram creates a filter BIO for testing for
// asynchronous state machines which consume datagram sockets. The read
// and write quota count in packets rather than bytes.
bssl::UniquePtr<BIO> AsyncBioCreateDatagram();
// AsyncBioAllowRead increments |bio|'s read quota by |count|.
void AsyncBioAllowRead(BIO *bio, size_t count);
// AsyncBioAllowWrite increments |bio|'s write quota by |count|.
void AsyncBioAllowWrite(BIO *bio, size_t count);
// AsyncBioEnforceWriteQuota configures where |bio| enforces its write quota.
void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce);
#endif // HEADER_ASYNC_BIO

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,619 @@
// Copyright (c) 2017, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_SSL_TEST_FUZZER
#define HEADER_SSL_TEST_FUZZER
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <openssl/bio.h>
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hpke.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include "../../crypto/internal.h"
#include "./fuzzer_tags.h"
namespace {
const uint8_t kP256KeyPKCS8[] = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0x43, 0x09, 0xc0, 0x67, 0x75, 0x21, 0x47, 0x9d, 0xa8, 0xfa, 0x16, 0xdf,
0x15, 0x73, 0x61, 0x34, 0x68, 0x6f, 0xe3, 0x8e, 0x47, 0x91, 0x95, 0xab,
0x79, 0x4a, 0x72, 0x14, 0xcb, 0xe2, 0x49, 0x4f, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0xde, 0x09, 0x08, 0x07, 0x03, 0x2e, 0x8f, 0x37, 0x9a, 0xd5,
0xad, 0xe5, 0xc6, 0x9d, 0xd4, 0x63, 0xc7, 0x4a, 0xe7, 0x20, 0xcb, 0x90,
0xa0, 0x1f, 0x18, 0x18, 0x72, 0xb5, 0x21, 0x88, 0x38, 0xc0, 0xdb, 0xba,
0xf6, 0x99, 0xd8, 0xa5, 0x3b, 0x83, 0xe9, 0xe3, 0xd5, 0x61, 0x99, 0x73,
0x42, 0xc6, 0x6c, 0xe8, 0x0a, 0x95, 0x40, 0x41, 0x3b, 0x0d, 0x10, 0xa7,
0x4a, 0x93, 0xdb, 0x5a, 0xe7, 0xec,
};
const uint8_t kOCSPResponse[] = {0x01, 0x02, 0x03, 0x04};
const uint8_t kSCT[] = {0x00, 0x06, 0x00, 0x04, 0x05, 0x06, 0x07, 0x08};
const uint8_t kCertificateDER[] = {
0x30, 0x82, 0x02, 0xff, 0x30, 0x82, 0x01, 0xe7, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x11, 0x00, 0xb1, 0x84, 0xee, 0x34, 0x99, 0x98, 0x76, 0xfb,
0x6f, 0xb2, 0x15, 0xc8, 0x47, 0x79, 0x05, 0x9b, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
0x12, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07,
0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, 0x30, 0x1e, 0x17, 0x0d, 0x31,
0x35, 0x31, 0x31, 0x30, 0x37, 0x30, 0x30, 0x32, 0x34, 0x35, 0x36, 0x5a,
0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x36, 0x30, 0x30, 0x32, 0x34,
0x35, 0x36, 0x5a, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
0x04, 0x0a, 0x13, 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, 0x30,
0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce, 0x47, 0xcb, 0x11,
0xbb, 0xd2, 0x9d, 0x8e, 0x9e, 0xd2, 0x1e, 0x14, 0xaf, 0xc7, 0xea, 0xb6,
0xc9, 0x38, 0x2a, 0x6f, 0xb3, 0x7e, 0xfb, 0xbc, 0xfc, 0x59, 0x42, 0xb9,
0x56, 0xf0, 0x4c, 0x3f, 0xf7, 0x31, 0x84, 0xbe, 0xac, 0x03, 0x9e, 0x71,
0x91, 0x85, 0xd8, 0x32, 0xbd, 0x00, 0xea, 0xac, 0x65, 0xf6, 0x03, 0xc8,
0x0f, 0x8b, 0xfd, 0x6e, 0x58, 0x88, 0x04, 0x41, 0x92, 0x74, 0xa6, 0x57,
0x2e, 0x8e, 0x88, 0xd5, 0x3d, 0xda, 0x14, 0x3e, 0x63, 0x88, 0x22, 0xe3,
0x53, 0xe9, 0xba, 0x39, 0x09, 0xac, 0xfb, 0xd0, 0x4c, 0xf2, 0x3c, 0x20,
0xd6, 0x97, 0xe6, 0xed, 0xf1, 0x62, 0x1e, 0xe5, 0xc9, 0x48, 0xa0, 0xca,
0x2e, 0x3c, 0x14, 0x5a, 0x82, 0xd4, 0xed, 0xb1, 0xe3, 0x43, 0xc1, 0x2a,
0x59, 0xa5, 0xb9, 0xc8, 0x48, 0xa7, 0x39, 0x23, 0x74, 0xa7, 0x37, 0xb0,
0x6f, 0xc3, 0x64, 0x99, 0x6c, 0xa2, 0x82, 0xc8, 0xf6, 0xdb, 0x86, 0x40,
0xce, 0xd1, 0x85, 0x9f, 0xce, 0x69, 0xf4, 0x15, 0x2a, 0x23, 0xca, 0xea,
0xb7, 0x7b, 0xdf, 0xfb, 0x43, 0x5f, 0xff, 0x7a, 0x49, 0x49, 0x0e, 0xe7,
0x02, 0x51, 0x45, 0x13, 0xe8, 0x90, 0x64, 0x21, 0x0c, 0x26, 0x2b, 0x5d,
0xfc, 0xe4, 0xb5, 0x86, 0x89, 0x43, 0x22, 0x4c, 0xf3, 0x3b, 0xf3, 0x09,
0xc4, 0xa4, 0x10, 0x80, 0xf2, 0x46, 0xe2, 0x46, 0x8f, 0x76, 0x50, 0xbf,
0xaf, 0x2b, 0x90, 0x1b, 0x78, 0xc7, 0xcf, 0xc1, 0x77, 0xd0, 0xfb, 0xa9,
0xfb, 0xc9, 0x66, 0x5a, 0xc5, 0x9b, 0x31, 0x41, 0x67, 0x01, 0xbe, 0x33,
0x10, 0xba, 0x05, 0x58, 0xed, 0x76, 0x53, 0xde, 0x5d, 0xc1, 0xe8, 0xbb,
0x9f, 0xf1, 0xcd, 0xfb, 0xdf, 0x64, 0x7f, 0xd7, 0x18, 0xab, 0x0f, 0x94,
0x28, 0x95, 0x4a, 0xcc, 0x6a, 0xa9, 0x50, 0xc7, 0x05, 0x47, 0x10, 0x41,
0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0e, 0x06,
0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05,
0xa0, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a,
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x0c,
0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
0x30, 0x19, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x12, 0x30, 0x10, 0x82,
0x0e, 0x66, 0x75, 0x7a, 0x7a, 0x2e, 0x62, 0x6f, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x73, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x92,
0xde, 0xef, 0x96, 0x06, 0x7b, 0xff, 0x71, 0x7d, 0x4e, 0xa0, 0x7d, 0xae,
0xb8, 0x22, 0xb4, 0x2c, 0xf7, 0x96, 0x9c, 0x37, 0x1d, 0x8f, 0xe7, 0xd9,
0x47, 0xff, 0x3f, 0xe9, 0x35, 0x95, 0x0e, 0xdd, 0xdc, 0x7f, 0xc8, 0x8a,
0x1e, 0x36, 0x1d, 0x38, 0x47, 0xfc, 0x76, 0xd2, 0x1f, 0x98, 0xa1, 0x36,
0xac, 0xc8, 0x70, 0x38, 0x0a, 0x3d, 0x51, 0x8d, 0x0f, 0x03, 0x1b, 0xef,
0x62, 0xa1, 0xcb, 0x2b, 0x4a, 0x8c, 0x12, 0x2b, 0x54, 0x50, 0x9a, 0x6b,
0xfe, 0xaf, 0xd9, 0xf6, 0xbf, 0x58, 0x11, 0x58, 0x5e, 0xe5, 0x86, 0x1e,
0x3b, 0x6b, 0x30, 0x7e, 0x72, 0x89, 0xe8, 0x6b, 0x7b, 0xb7, 0xaf, 0xef,
0x8b, 0xa9, 0x3e, 0xb0, 0xcd, 0x0b, 0xef, 0xb0, 0x0c, 0x96, 0x2b, 0xc5,
0x3b, 0xd5, 0xf1, 0xc2, 0xae, 0x3a, 0x60, 0xd9, 0x0f, 0x75, 0x37, 0x55,
0x4d, 0x62, 0xd2, 0xed, 0x96, 0xac, 0x30, 0x6b, 0xda, 0xa1, 0x48, 0x17,
0x96, 0x23, 0x85, 0x9a, 0x57, 0x77, 0xe9, 0x22, 0xa2, 0x37, 0x03, 0xba,
0x49, 0x77, 0x40, 0x3b, 0x76, 0x4b, 0xda, 0xc1, 0x04, 0x57, 0x55, 0x34,
0x22, 0x83, 0x45, 0x29, 0xab, 0x2e, 0x11, 0xff, 0x0d, 0xab, 0x55, 0xb1,
0xa7, 0x58, 0x59, 0x05, 0x25, 0xf9, 0x1e, 0x3d, 0xb7, 0xac, 0x04, 0x39,
0x2c, 0xf9, 0xaf, 0xb8, 0x68, 0xfb, 0x8e, 0x35, 0x71, 0x32, 0xff, 0x70,
0xe9, 0x46, 0x6d, 0x5c, 0x06, 0x90, 0x88, 0x23, 0x48, 0x0c, 0x50, 0xeb,
0x0a, 0xa9, 0xae, 0xe8, 0xfc, 0xbe, 0xa5, 0x76, 0x94, 0xd7, 0x64, 0x22,
0x38, 0x98, 0x17, 0xa4, 0x3a, 0xa7, 0x59, 0x9f, 0x1d, 0x3b, 0x75, 0x90,
0x1a, 0x81, 0xef, 0x19, 0xfb, 0x2b, 0xb7, 0xa7, 0x64, 0x61, 0x22, 0xa4,
0x6f, 0x7b, 0xfa, 0x58, 0xbb, 0x8c, 0x4e, 0x77, 0x67, 0xd0, 0x5d, 0x58,
0x76, 0x8a, 0xbb,
};
const uint8_t kRSAPrivateKeyDER[] = {
0x30, 0x82, 0x04, 0xa5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
0xce, 0x47, 0xcb, 0x11, 0xbb, 0xd2, 0x9d, 0x8e, 0x9e, 0xd2, 0x1e, 0x14,
0xaf, 0xc7, 0xea, 0xb6, 0xc9, 0x38, 0x2a, 0x6f, 0xb3, 0x7e, 0xfb, 0xbc,
0xfc, 0x59, 0x42, 0xb9, 0x56, 0xf0, 0x4c, 0x3f, 0xf7, 0x31, 0x84, 0xbe,
0xac, 0x03, 0x9e, 0x71, 0x91, 0x85, 0xd8, 0x32, 0xbd, 0x00, 0xea, 0xac,
0x65, 0xf6, 0x03, 0xc8, 0x0f, 0x8b, 0xfd, 0x6e, 0x58, 0x88, 0x04, 0x41,
0x92, 0x74, 0xa6, 0x57, 0x2e, 0x8e, 0x88, 0xd5, 0x3d, 0xda, 0x14, 0x3e,
0x63, 0x88, 0x22, 0xe3, 0x53, 0xe9, 0xba, 0x39, 0x09, 0xac, 0xfb, 0xd0,
0x4c, 0xf2, 0x3c, 0x20, 0xd6, 0x97, 0xe6, 0xed, 0xf1, 0x62, 0x1e, 0xe5,
0xc9, 0x48, 0xa0, 0xca, 0x2e, 0x3c, 0x14, 0x5a, 0x82, 0xd4, 0xed, 0xb1,
0xe3, 0x43, 0xc1, 0x2a, 0x59, 0xa5, 0xb9, 0xc8, 0x48, 0xa7, 0x39, 0x23,
0x74, 0xa7, 0x37, 0xb0, 0x6f, 0xc3, 0x64, 0x99, 0x6c, 0xa2, 0x82, 0xc8,
0xf6, 0xdb, 0x86, 0x40, 0xce, 0xd1, 0x85, 0x9f, 0xce, 0x69, 0xf4, 0x15,
0x2a, 0x23, 0xca, 0xea, 0xb7, 0x7b, 0xdf, 0xfb, 0x43, 0x5f, 0xff, 0x7a,
0x49, 0x49, 0x0e, 0xe7, 0x02, 0x51, 0x45, 0x13, 0xe8, 0x90, 0x64, 0x21,
0x0c, 0x26, 0x2b, 0x5d, 0xfc, 0xe4, 0xb5, 0x86, 0x89, 0x43, 0x22, 0x4c,
0xf3, 0x3b, 0xf3, 0x09, 0xc4, 0xa4, 0x10, 0x80, 0xf2, 0x46, 0xe2, 0x46,
0x8f, 0x76, 0x50, 0xbf, 0xaf, 0x2b, 0x90, 0x1b, 0x78, 0xc7, 0xcf, 0xc1,
0x77, 0xd0, 0xfb, 0xa9, 0xfb, 0xc9, 0x66, 0x5a, 0xc5, 0x9b, 0x31, 0x41,
0x67, 0x01, 0xbe, 0x33, 0x10, 0xba, 0x05, 0x58, 0xed, 0x76, 0x53, 0xde,
0x5d, 0xc1, 0xe8, 0xbb, 0x9f, 0xf1, 0xcd, 0xfb, 0xdf, 0x64, 0x7f, 0xd7,
0x18, 0xab, 0x0f, 0x94, 0x28, 0x95, 0x4a, 0xcc, 0x6a, 0xa9, 0x50, 0xc7,
0x05, 0x47, 0x10, 0x41, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
0x01, 0x00, 0xa8, 0x47, 0xb9, 0x4a, 0x06, 0x47, 0x93, 0x71, 0x3d, 0xef,
0x7b, 0xca, 0xb4, 0x7c, 0x0a, 0xe6, 0x82, 0xd0, 0xe7, 0x0d, 0xa9, 0x08,
0xf6, 0xa4, 0xfd, 0xd8, 0x73, 0xae, 0x6f, 0x56, 0x29, 0x5e, 0x25, 0x72,
0xa8, 0x30, 0x44, 0x73, 0xcf, 0x56, 0x26, 0xb9, 0x61, 0xde, 0x42, 0x81,
0xf4, 0xf0, 0x1f, 0x5d, 0xcb, 0x47, 0xf2, 0x26, 0xe9, 0xe0, 0x93, 0x28,
0xa3, 0x10, 0x3b, 0x42, 0x1e, 0x51, 0x11, 0x12, 0x06, 0x5e, 0xaf, 0xce,
0xb0, 0xa5, 0x14, 0xdd, 0x82, 0x58, 0xa1, 0xa4, 0x12, 0xdf, 0x65, 0x1d,
0x51, 0x70, 0x64, 0xd5, 0x58, 0x68, 0x11, 0xa8, 0x6a, 0x23, 0xc2, 0xbf,
0xa1, 0x25, 0x24, 0x47, 0xb3, 0xa4, 0x3c, 0x83, 0x96, 0xb7, 0x1f, 0xf4,
0x44, 0xd4, 0xd1, 0xe9, 0xfc, 0x33, 0x68, 0x5e, 0xe2, 0x68, 0x99, 0x9c,
0x91, 0xe8, 0x72, 0xc9, 0xd7, 0x8c, 0x80, 0x20, 0x8e, 0x77, 0x83, 0x4d,
0xe4, 0xab, 0xf9, 0x74, 0xa1, 0xdf, 0xd3, 0xc0, 0x0d, 0x5b, 0x05, 0x51,
0xc2, 0x6f, 0xb2, 0x91, 0x02, 0xec, 0xc0, 0x02, 0x1a, 0x5c, 0x91, 0x05,
0xf1, 0xe3, 0xfa, 0x65, 0xc2, 0xad, 0x24, 0xe6, 0xe5, 0x3c, 0xb6, 0x16,
0xf1, 0xa1, 0x67, 0x1a, 0x9d, 0x37, 0x56, 0xbf, 0x01, 0xd7, 0x3b, 0x35,
0x30, 0x57, 0x73, 0xf4, 0xf0, 0x5e, 0xa7, 0xe8, 0x0a, 0xc1, 0x94, 0x17,
0xcf, 0x0a, 0xbd, 0xf5, 0x31, 0xa7, 0x2d, 0xf7, 0xf5, 0xd9, 0x8c, 0xc2,
0x01, 0xbd, 0xda, 0x16, 0x8e, 0xb9, 0x30, 0x40, 0xa6, 0x6e, 0xbd, 0xcd,
0x4d, 0x84, 0x67, 0x4e, 0x0b, 0xce, 0xd5, 0xef, 0xf8, 0x08, 0x63, 0x02,
0xc6, 0xc7, 0xf7, 0x67, 0x92, 0xe2, 0x23, 0x9d, 0x27, 0x22, 0x1d, 0xc6,
0x67, 0x5e, 0x66, 0xbf, 0x03, 0xb8, 0xa9, 0x67, 0xd4, 0x39, 0xd8, 0x75,
0xfa, 0xe8, 0xed, 0x56, 0xb8, 0x81, 0x02, 0x81, 0x81, 0x00, 0xf7, 0x46,
0x68, 0xc6, 0x13, 0xf8, 0xba, 0x0f, 0x83, 0xdb, 0x05, 0xa8, 0x25, 0x00,
0x70, 0x9c, 0x9e, 0x8b, 0x12, 0x34, 0x0d, 0x96, 0xcf, 0x0d, 0x98, 0x9b,
0x8d, 0x9c, 0x96, 0x78, 0xd1, 0x3c, 0x01, 0x8c, 0xb9, 0x35, 0x5c, 0x20,
0x42, 0xb4, 0x38, 0xe3, 0xd6, 0x54, 0xe7, 0x55, 0xd6, 0x26, 0x8a, 0x0c,
0xf6, 0x1f, 0xe0, 0x04, 0xc1, 0x22, 0x42, 0x19, 0x61, 0xc4, 0x94, 0x7c,
0x07, 0x2e, 0x80, 0x52, 0xfe, 0x8d, 0xe6, 0x92, 0x3a, 0x91, 0xfe, 0x72,
0x99, 0xe1, 0x2a, 0x73, 0x76, 0xb1, 0x24, 0x20, 0x67, 0xde, 0x28, 0xcb,
0x0e, 0xe6, 0x52, 0xb5, 0xfa, 0xfb, 0x8b, 0x1e, 0x6a, 0x1d, 0x09, 0x26,
0xb9, 0xa7, 0x61, 0xba, 0xf8, 0x79, 0xd2, 0x66, 0x57, 0x28, 0xd7, 0x31,
0xb5, 0x0b, 0x27, 0x19, 0x1e, 0x6f, 0x46, 0xfc, 0x54, 0x95, 0xeb, 0x78,
0x01, 0xb6, 0xd9, 0x79, 0x5a, 0x4d, 0x02, 0x81, 0x81, 0x00, 0xd5, 0x8f,
0x16, 0x53, 0x2f, 0x57, 0x93, 0xbf, 0x09, 0x75, 0xbf, 0x63, 0x40, 0x3d,
0x27, 0xfd, 0x23, 0x21, 0xde, 0x9b, 0xe9, 0x73, 0x3f, 0x49, 0x02, 0xd2,
0x38, 0x96, 0xcf, 0xc3, 0xba, 0x92, 0x07, 0x87, 0x52, 0xa9, 0x35, 0xe3,
0x0c, 0xe4, 0x2f, 0x05, 0x7b, 0x37, 0xa5, 0x40, 0x9c, 0x3b, 0x94, 0xf7,
0xad, 0xa0, 0xee, 0x3a, 0xa8, 0xfb, 0x1f, 0x11, 0x1f, 0xd8, 0x9a, 0x80,
0x42, 0x3d, 0x7f, 0xa4, 0xb8, 0x9a, 0xaa, 0xea, 0x72, 0xc1, 0xe3, 0xed,
0x06, 0x60, 0x92, 0x37, 0xf9, 0xba, 0xfb, 0x9e, 0xed, 0x05, 0xa6, 0xd4,
0x72, 0x68, 0x4f, 0x63, 0xfe, 0xd6, 0x10, 0x0d, 0x4f, 0x0a, 0x93, 0xc6,
0xb9, 0xd7, 0xaf, 0xfd, 0xd9, 0x57, 0x7d, 0xcb, 0x75, 0xe8, 0x93, 0x2b,
0xae, 0x4f, 0xea, 0xd7, 0x30, 0x0b, 0x58, 0x44, 0x82, 0x0f, 0x84, 0x5d,
0x62, 0x11, 0x78, 0xea, 0x5f, 0xc5, 0x02, 0x81, 0x81, 0x00, 0x82, 0x0c,
0xc1, 0xe6, 0x0b, 0x72, 0xf1, 0x48, 0x5f, 0xac, 0xbd, 0x98, 0xe5, 0x7d,
0x09, 0xbd, 0x15, 0x95, 0x47, 0x09, 0xa1, 0x6c, 0x03, 0x91, 0xbf, 0x05,
0x70, 0xc1, 0x3e, 0x52, 0x64, 0x99, 0x0e, 0xa7, 0x98, 0x70, 0xfb, 0xf6,
0xeb, 0x9e, 0x25, 0x9d, 0x8e, 0x88, 0x30, 0xf2, 0xf0, 0x22, 0x6c, 0xd0,
0xcc, 0x51, 0x8f, 0x5c, 0x70, 0xc7, 0x37, 0xc4, 0x69, 0xab, 0x1d, 0xfc,
0xed, 0x3a, 0x03, 0xbb, 0xa2, 0xad, 0xb6, 0xea, 0x89, 0x6b, 0x67, 0x4b,
0x96, 0xaa, 0xd9, 0xcc, 0xc8, 0x4b, 0xfa, 0x18, 0x21, 0x08, 0xb2, 0xa3,
0xb9, 0x3e, 0x61, 0x99, 0xdc, 0x5a, 0x97, 0x9c, 0x73, 0x6a, 0xb9, 0xf9,
0x68, 0x03, 0x24, 0x5f, 0x55, 0x77, 0x9c, 0xb4, 0xbe, 0x7a, 0x78, 0x53,
0x68, 0x48, 0x69, 0x53, 0xc8, 0xb1, 0xf5, 0xbf, 0x98, 0x2d, 0x11, 0x1e,
0x98, 0xa8, 0x36, 0x50, 0xa0, 0xb1, 0x02, 0x81, 0x81, 0x00, 0x90, 0x88,
0x30, 0x71, 0xc7, 0xfe, 0x9b, 0x6d, 0x95, 0x37, 0x6d, 0x79, 0xfc, 0x85,
0xe7, 0x44, 0x78, 0xbc, 0x79, 0x6e, 0x47, 0x86, 0xc9, 0xf3, 0xdd, 0xc6,
0xec, 0xa9, 0x94, 0x9f, 0x40, 0xeb, 0x87, 0xd0, 0xdb, 0xee, 0xcd, 0x1b,
0x87, 0x23, 0xff, 0x76, 0xd4, 0x37, 0x8a, 0xcd, 0xb9, 0x6e, 0xd1, 0x98,
0xf6, 0x97, 0x8d, 0xe3, 0x81, 0x6d, 0xc3, 0x4e, 0xd1, 0xa0, 0xc4, 0x9f,
0xbd, 0x34, 0xe5, 0xe8, 0x53, 0x4f, 0xca, 0x10, 0xb5, 0xed, 0xe7, 0x16,
0x09, 0x54, 0xde, 0x60, 0xa7, 0xd1, 0x16, 0x6e, 0x2e, 0xb7, 0xbe, 0x7a,
0xd5, 0x9b, 0x26, 0xef, 0xe4, 0x0e, 0x77, 0xfa, 0xa9, 0xdd, 0xdc, 0xb9,
0x88, 0x19, 0x23, 0x70, 0xc7, 0xe1, 0x60, 0xaf, 0x8c, 0x73, 0x04, 0xf7,
0x71, 0x17, 0x81, 0x36, 0x75, 0xbb, 0x97, 0xd7, 0x75, 0xb6, 0x8e, 0xbc,
0xac, 0x9c, 0x6a, 0x9b, 0x24, 0x89, 0x02, 0x81, 0x80, 0x5a, 0x2b, 0xc7,
0x6b, 0x8c, 0x65, 0xdb, 0x04, 0x73, 0xab, 0x25, 0xe1, 0x5b, 0xbc, 0x3c,
0xcf, 0x5a, 0x3c, 0x04, 0xae, 0x97, 0x2e, 0xfd, 0xa4, 0x97, 0x1f, 0x05,
0x17, 0x27, 0xac, 0x7c, 0x30, 0x85, 0xb4, 0x82, 0x3f, 0x5b, 0xb7, 0x94,
0x3b, 0x7f, 0x6c, 0x0c, 0xc7, 0x16, 0xc6, 0xa0, 0xbd, 0x80, 0xb0, 0x81,
0xde, 0xa0, 0x23, 0xa6, 0xf6, 0x75, 0x33, 0x51, 0x35, 0xa2, 0x75, 0x55,
0x70, 0x4d, 0x42, 0xbb, 0xcf, 0x54, 0xe4, 0xdb, 0x2d, 0x88, 0xa0, 0x7a,
0xf2, 0x17, 0xa7, 0xdd, 0x13, 0x44, 0x9f, 0x5f, 0x6b, 0x2c, 0x42, 0x42,
0x8b, 0x13, 0x4d, 0xf9, 0x5b, 0xf8, 0x33, 0x42, 0xd9, 0x9e, 0x50, 0x1c,
0x7c, 0xbc, 0xfa, 0x62, 0x85, 0x0b, 0xcf, 0x99, 0xda, 0x9e, 0x04, 0x90,
0xb2, 0xc6, 0xb2, 0x0a, 0x2a, 0x7c, 0x6d, 0x6a, 0x40, 0xfc, 0xf5, 0x50,
0x98, 0x46, 0x89, 0x82, 0x40,
};
const uint8_t kALPNProtocols[] = {
0x01, 'a', 0x02, 'a', 'a', 0x03, 'a', 'a', 'a',
};
const uint8_t kECHKey[] = {
0x35, 0x6d, 0x45, 0x06, 0xb3, 0x88, 0x89, 0x2e, 0xd6, 0x87, 0x84,
0xd2, 0x2d, 0x6f, 0x83, 0x48, 0xad, 0xf2, 0xfd, 0x08, 0x51, 0x73,
0x10, 0xa0, 0xb8, 0xdd, 0xe9, 0x96, 0x6a, 0xde, 0xbc, 0x82,
};
int ALPNSelectCallback(SSL *ssl, const uint8_t **out, uint8_t *out_len,
const uint8_t *in, unsigned in_len, void *arg) {
static const uint8_t kProtocol[] = {'a', 'a'};
*out = kProtocol;
*out_len = sizeof(kProtocol);
return SSL_TLSEXT_ERR_OK;
}
int NPNSelectCallback(SSL *ssl, uint8_t **out, uint8_t *out_len,
const uint8_t *in, unsigned in_len, void *arg) {
static const uint8_t kProtocol[] = {'a', 'a'};
*out = const_cast<uint8_t *>(kProtocol);
*out_len = sizeof(kProtocol);
return SSL_TLSEXT_ERR_OK;
}
int NPNAdvertiseCallback(SSL *ssl, const uint8_t **out, unsigned *out_len,
void *arg) {
static const uint8_t kProtocols[] = {
0x01, 'a', 0x02, 'a', 'a', 0x03, 'a', 'a', 'a',
};
*out = kProtocols;
*out_len = sizeof(kProtocols);
return SSL_TLSEXT_ERR_OK;
}
class TLSFuzzer {
public:
enum Protocol {
kTLS,
kDTLS,
};
enum Role {
kClient,
kServer,
};
TLSFuzzer(Protocol protocol, Role role)
: debug_(getenv("BORINGSSL_FUZZER_DEBUG") != nullptr),
protocol_(protocol),
role_(role) {
if (!Init()) {
abort();
}
}
static void MoveBIOs(SSL *dest, SSL *src) {
BIO *rbio = SSL_get_rbio(src);
BIO_up_ref(rbio);
SSL_set0_rbio(dest, rbio);
BIO *wbio = SSL_get_wbio(src);
BIO_up_ref(wbio);
SSL_set0_wbio(dest, wbio);
SSL_set0_rbio(src, nullptr);
SSL_set0_wbio(src, nullptr);
}
int TestOneInput(const uint8_t *buf, size_t len) {
RAND_reset_for_fuzzing();
CBS cbs;
CBS_init(&cbs, buf, len);
bssl::UniquePtr<SSL> ssl = SetupTest(&cbs);
if (!ssl) {
if (debug_) {
fprintf(stderr, "Error parsing parameters.\n");
}
return 0;
}
if (role_ == kClient) {
SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely);
SSL_set_tlsext_host_name(ssl.get(), "hostname");
}
// ssl_handoff may or may not be used.
bssl::UniquePtr<SSL> ssl_handoff(SSL_new(ctx_.get()));
bssl::UniquePtr<SSL> ssl_handback(SSL_new(ctx_.get()));
SSL_set_accept_state(ssl_handoff.get());
SSL_set0_rbio(ssl.get(), MakeBIO(CBS_data(&cbs), CBS_len(&cbs)).release());
SSL_set0_wbio(ssl.get(), BIO_new(BIO_s_mem()));
SSL *ssl_handshake = ssl.get();
bool handshake_successful = false;
bool handback_successful = false;
for (;;) {
int ret = SSL_do_handshake(ssl_handshake);
if (ret < 0 && SSL_get_error(ssl_handshake, ret) == SSL_ERROR_HANDOFF) {
MoveBIOs(ssl_handoff.get(), ssl.get());
// Ordinarily we would call SSL_serialize_handoff(ssl.get(). But for
// fuzzing, use the serialized handoff that's getting fuzzed.
if (!bssl::SSL_apply_handoff(ssl_handoff.get(), handoff_)) {
if (debug_) {
fprintf(stderr, "Handoff failed.\n");
}
break;
}
ssl_handshake = ssl_handoff.get();
} else if (ret < 0 &&
SSL_get_error(ssl_handshake, ret) == SSL_ERROR_HANDBACK) {
MoveBIOs(ssl_handback.get(), ssl_handoff.get());
if (!bssl::SSL_apply_handback(ssl_handback.get(), handback_)) {
if (debug_) {
fprintf(stderr, "Handback failed.\n");
}
break;
}
handback_successful = true;
ssl_handshake = ssl_handback.get();
} else {
handshake_successful = ret == 1;
break;
}
}
if (debug_) {
if (!handshake_successful) {
fprintf(stderr, "Handshake failed.\n");
} else if (handback_successful) {
fprintf(stderr, "Handback successful.\n");
}
}
if (handshake_successful) {
// Keep reading application data until error or EOF.
uint8_t tmp[1024];
for (;;) {
if (SSL_read(ssl_handshake, tmp, sizeof(tmp)) <= 0) {
break;
}
}
}
if (debug_) {
ERR_print_errors_fp(stderr);
}
ERR_clear_error();
return 0;
}
private:
// Init initializes |ctx_| with settings common to all inputs.
bool Init() {
ctx_.reset(SSL_CTX_new(protocol_ == kDTLS ? DTLS_method() : TLS_method()));
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
bssl::UniquePtr<RSA> privkey(RSA_private_key_from_bytes(
kRSAPrivateKeyDER, sizeof(kRSAPrivateKeyDER)));
if (!ctx_ || !privkey || !pkey ||
!EVP_PKEY_set1_RSA(pkey.get(), privkey.get()) ||
!SSL_CTX_use_PrivateKey(ctx_.get(), pkey.get())) {
return false;
}
const uint8_t *bufp = kCertificateDER;
bssl::UniquePtr<X509> cert(d2i_X509(NULL, &bufp, sizeof(kCertificateDER)));
if (!cert ||
!SSL_CTX_use_certificate(ctx_.get(), cert.get()) ||
!SSL_CTX_set_ocsp_response(ctx_.get(), kOCSPResponse,
sizeof(kOCSPResponse)) ||
!SSL_CTX_set_signed_cert_timestamp_list(ctx_.get(), kSCT,
sizeof(kSCT))) {
return false;
}
// When accepting peer certificates, allow any certificate.
SSL_CTX_set_cert_verify_callback(
ctx_.get(),
[](X509_STORE_CTX *store_ctx, void *arg) -> int { return 1; }, nullptr);
SSL_CTX_enable_signed_cert_timestamps(ctx_.get());
SSL_CTX_enable_ocsp_stapling(ctx_.get());
// Enable versions and ciphers that are off by default.
if (!SSL_CTX_set_strict_cipher_list(ctx_.get(), "ALL:NULL-SHA")) {
return false;
}
static const int kGroups[] = {NID_X25519, NID_X9_62_prime256v1,
NID_secp384r1, NID_secp521r1};
if (!SSL_CTX_set1_groups(ctx_.get(), kGroups,
OPENSSL_ARRAY_SIZE(kGroups))) {
return false;
}
SSL_CTX_set_early_data_enabled(ctx_.get(), 1);
SSL_CTX_set_next_proto_select_cb(ctx_.get(), NPNSelectCallback, nullptr);
SSL_CTX_set_next_protos_advertised_cb(ctx_.get(), NPNAdvertiseCallback,
nullptr);
SSL_CTX_set_alpn_select_cb(ctx_.get(), ALPNSelectCallback, nullptr);
if (SSL_CTX_set_alpn_protos(ctx_.get(), kALPNProtocols,
sizeof(kALPNProtocols)) != 0) {
return false;
}
CBS cbs;
CBS_init(&cbs, kP256KeyPKCS8, sizeof(kP256KeyPKCS8));
pkey.reset(EVP_parse_private_key(&cbs));
if (!pkey || !SSL_CTX_set1_tls_channel_id(ctx_.get(), pkey.get())) {
return false;
}
SSL_CTX_set_tls_channel_id_enabled(ctx_.get(), 1);
if (role_ == kServer) {
bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
bssl::ScopedEVP_HPKE_KEY key;
uint8_t *ech_config;
size_t ech_config_len;
if (!keys ||
!EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(), kECHKey,
sizeof(kECHKey)) ||
// Match |echConfig| in |addEncryptedClientHelloTests| from runner.go.
!SSL_marshal_ech_config(&ech_config, &ech_config_len,
/*config_id=*/42, key.get(), "public.example",
/*max_name_len=*/64)) {
return false;
}
bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
if (!SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/true, ech_config,
ech_config_len, key.get()) ||
!SSL_CTX_set1_ech_keys(ctx_.get(), keys.get())) {
return false;
}
}
return true;
}
// SetupTest parses parameters from |cbs| and returns a newly-configured |SSL|
// object or nullptr on error. On success, the caller should feed the
// remaining input in |cbs| to the SSL stack.
bssl::UniquePtr<SSL> SetupTest(CBS *cbs) {
// |ctx| is shared between runs, so we must clear any modifications to it
// made later on in this function.
SSL_CTX_flush_sessions(ctx_.get(), 0);
handoff_ = {};
handback_ = {};
bssl::UniquePtr<SSL> ssl(SSL_new(ctx_.get()));
if (role_ == kServer) {
SSL_set_accept_state(ssl.get());
} else {
SSL_set_connect_state(ssl.get());
}
for (;;) {
uint16_t tag;
if (!CBS_get_u16(cbs, &tag)) {
return nullptr;
}
switch (tag) {
case kDataTag:
return ssl;
case kSessionTag: {
CBS data;
if (!CBS_get_u24_length_prefixed(cbs, &data)) {
return nullptr;
}
bssl::UniquePtr<SSL_SESSION> session(SSL_SESSION_from_bytes(
CBS_data(&data), CBS_len(&data), ctx_.get()));
if (!session) {
return nullptr;
}
if (role_ == kServer) {
SSL_CTX_add_session(ctx_.get(), session.get());
} else {
SSL_set_session(ssl.get(), session.get());
}
break;
}
case kRequestClientCert:
if (role_ == kClient) {
return nullptr;
}
SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, nullptr);
break;
case kHandoffTag: {
CBS handoff;
if (!CBS_get_u24_length_prefixed(cbs, &handoff)) {
return nullptr;
}
handoff_.assign(CBS_data(&handoff),
CBS_data(&handoff) + CBS_len(&handoff));
bssl::SSL_set_handoff_mode(ssl.get(), 1);
break;
}
case kHandbackTag: {
CBS handback;
if (!CBS_get_u24_length_prefixed(cbs, &handback)) {
return nullptr;
}
handback_.assign(CBS_data(&handback),
CBS_data(&handback) + CBS_len(&handback));
bssl::SSL_set_handoff_mode(ssl.get(), 1);
break;
}
case kHintsTag: {
CBS hints;
if (!CBS_get_u24_length_prefixed(cbs, &hints)) {
return nullptr;
}
SSL_set_handshake_hints(ssl.get(), CBS_data(&hints), CBS_len(&hints));
break;
}
default:
return nullptr;
}
}
}
struct BIOData {
Protocol protocol;
CBS cbs;
};
bssl::UniquePtr<BIO> MakeBIO(const uint8_t *in, size_t len) {
BIOData *b = new BIOData;
b->protocol = protocol_;
CBS_init(&b->cbs, in, len);
bssl::UniquePtr<BIO> bio(BIO_new(&kBIOMethod));
bio->init = 1;
bio->ptr = b;
return bio;
}
static int BIORead(BIO *bio, char *out, int len) {
assert(bio->method == &kBIOMethod);
BIOData *b = reinterpret_cast<BIOData *>(bio->ptr);
if (b->protocol == kTLS) {
len = std::min(static_cast<size_t>(len), CBS_len(&b->cbs));
memcpy(out, CBS_data(&b->cbs), len);
CBS_skip(&b->cbs, len);
return len;
}
// Preserve packet boundaries for DTLS.
CBS packet;
if (!CBS_get_u24_length_prefixed(&b->cbs, &packet)) {
return -1;
}
len = std::min(static_cast<size_t>(len), CBS_len(&packet));
memcpy(out, CBS_data(&packet), len);
return len;
}
static int BIODestroy(BIO *bio) {
assert(bio->method == &kBIOMethod);
BIOData *b = reinterpret_cast<BIOData *>(bio->ptr);
delete b;
return 1;
}
static const BIO_METHOD kBIOMethod;
bool debug_;
Protocol protocol_;
Role role_;
bssl::UniquePtr<SSL_CTX> ctx_;
std::vector<uint8_t> handoff_, handback_;
};
const BIO_METHOD TLSFuzzer::kBIOMethod = {
0, // type
nullptr, // name
nullptr, // bwrite
TLSFuzzer::BIORead,
nullptr, // bputs
nullptr, // bgets
nullptr, // ctrl
nullptr, // create
TLSFuzzer::BIODestroy,
nullptr, // callback_ctrl
};
} // namespace
#endif // HEADER_SSL_TEST_FUZZER

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2017, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_SSL_TEST_FUZZER_TAGS
#define HEADER_SSL_TEST_FUZZER_TAGS
#include <stdint.h>
// SSL fuzzer tag constants.
//
// The TLS client and server fuzzers coordinate with bssl_shim on a common
// format to encode configuration parameters in a fuzzer file. To add a new
// configuration, define a tag, update |SetupTest| in fuzzer.h to parse it, and
// update |SettingsWriter| in bssl_shim to serialize it. Finally, record
// transcripts from a test run, and use the BORINGSSL_FUZZER_DEBUG environment
// variable to confirm the transcripts are compatible.
// kDataTag denotes that the remainder of the input should be passed to the TLS
// stack.
static const uint16_t kDataTag = 0;
// kSessionTag is followed by a u24-length-prefixed serialized SSL_SESSION to
// resume.
static const uint16_t kSessionTag = 1;
// kRequestClientCert denotes that the server should request client
// certificates.
static const uint16_t kRequestClientCert = 2;
// kHandoffTag is followed by the output of |SSL_serialize_handoff|.
static const uint16_t kHandoffTag = 3;
// kHandbackTag is followed by te output of |SSL_serialize_handback|.
static const uint16_t kHandbackTag = 4;
// kHintsTag is followed by the output of |SSL_serialize_handshake_hints|.
static const uint16_t kHintsTag = 5;
#endif // HEADER_SSL_TEST_FUZZER_TAGS

View File

@@ -0,0 +1,706 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#include "handshake_util.h"
#include <assert.h>
#if defined(HANDSHAKER_SUPPORTED)
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#include <functional>
#include <map>
#include <vector>
#include "async_bio.h"
#include "packeted_bio.h"
#include "test_config.h"
#include "test_state.h"
#include <openssl/bytestring.h>
#include <openssl/ssl.h>
using namespace bssl;
bool RetryAsync(SSL *ssl, int ret) {
const TestConfig *config = GetTestConfig(ssl);
TestState *test_state = GetTestState(ssl);
if (ret >= 0) {
return false;
}
int ssl_err = SSL_get_error(ssl, ret);
if (ssl_err == SSL_ERROR_WANT_RENEGOTIATE && config->renegotiate_explicit) {
test_state->explicit_renegotiates++;
return SSL_renegotiate(ssl);
}
if (test_state->quic_transport && ssl_err == SSL_ERROR_WANT_READ) {
return test_state->quic_transport->ReadHandshake();
}
if (!config->async) {
// Only asynchronous tests should trigger other retries.
return false;
}
if (test_state->packeted_bio != nullptr &&
PacketedBioAdvanceClock(test_state->packeted_bio)) {
// The DTLS retransmit logic silently ignores write failures. So the test
// may progress, allow writes through synchronously.
AsyncBioEnforceWriteQuota(test_state->async_bio, false);
int timeout_ret = DTLSv1_handle_timeout(ssl);
AsyncBioEnforceWriteQuota(test_state->async_bio, true);
if (timeout_ret < 0) {
fprintf(stderr, "Error retransmitting.\n");
return false;
}
return true;
}
// See if we needed to read or write more. If so, allow one byte through on
// the appropriate end to maximally stress the state machine.
switch (ssl_err) {
case SSL_ERROR_WANT_READ:
AsyncBioAllowRead(test_state->async_bio, 1);
return true;
case SSL_ERROR_WANT_WRITE:
AsyncBioAllowWrite(test_state->async_bio, 1);
return true;
case SSL_ERROR_WANT_X509_LOOKUP:
test_state->cert_ready = true;
return true;
case SSL_ERROR_PENDING_SESSION:
test_state->session = std::move(test_state->pending_session);
return true;
case SSL_ERROR_PENDING_CERTIFICATE:
test_state->early_callback_ready = true;
return true;
case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
test_state->private_key_retries++;
return true;
case SSL_ERROR_WANT_CERTIFICATE_VERIFY:
test_state->custom_verify_ready = true;
return true;
default:
return false;
}
}
int CheckIdempotentError(const char *name, SSL *ssl,
std::function<int()> func) {
int ret = func();
int ssl_err = SSL_get_error(ssl, ret);
uint32_t err = ERR_peek_error();
if (ssl_err == SSL_ERROR_SSL || ssl_err == SSL_ERROR_ZERO_RETURN) {
int ret2 = func();
int ssl_err2 = SSL_get_error(ssl, ret2);
uint32_t err2 = ERR_peek_error();
if (ret != ret2 || ssl_err != ssl_err2 || err != err2) {
fprintf(stderr, "Repeating %s did not replay the error.\n", name);
char buf[256];
ERR_error_string_n(err, buf, sizeof(buf));
fprintf(stderr, "Wanted: %d %d %s\n", ret, ssl_err, buf);
ERR_error_string_n(err2, buf, sizeof(buf));
fprintf(stderr, "Got: %d %d %s\n", ret2, ssl_err2, buf);
// runner treats exit code 90 as always failing. Otherwise, it may
// accidentally consider the result an expected protocol failure.
exit(90);
}
}
return ret;
}
#if defined(HANDSHAKER_SUPPORTED)
// MoveBIOs moves the |BIO|s of |src| to |dst|. It is used for handoff.
static void MoveBIOs(SSL *dest, SSL *src) {
BIO *rbio = SSL_get_rbio(src);
BIO_up_ref(rbio);
SSL_set0_rbio(dest, rbio);
BIO *wbio = SSL_get_wbio(src);
BIO_up_ref(wbio);
SSL_set0_wbio(dest, wbio);
SSL_set0_rbio(src, nullptr);
SSL_set0_wbio(src, nullptr);
}
static bool HandoffReady(SSL *ssl, int ret) {
return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDOFF;
}
static ssize_t read_eintr(int fd, void *out, size_t len) {
ssize_t ret;
do {
ret = read(fd, out, len);
} while (ret < 0 && errno == EINTR);
return ret;
}
static ssize_t write_eintr(int fd, const void *in, size_t len) {
ssize_t ret;
do {
ret = write(fd, in, len);
} while (ret < 0 && errno == EINTR);
return ret;
}
static ssize_t waitpid_eintr(pid_t pid, int *wstatus, int options) {
pid_t ret;
do {
ret = waitpid(pid, wstatus, options);
} while (ret < 0 && errno == EINTR);
return ret;
}
// Proxy relays data between |socket|, which is connected to the client, and the
// handshaker, which is connected to the numerically specified file descriptors,
// until the handshaker returns control.
static bool Proxy(BIO *socket, bool async, int control, int rfd, int wfd) {
for (;;) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(wfd, &rfds);
FD_SET(control, &rfds);
int fd_max = wfd > control ? wfd : control;
if (select(fd_max + 1, &rfds, nullptr, nullptr, nullptr) == -1) {
perror("select");
return false;
}
char buf[64];
ssize_t bytes;
if (FD_ISSET(wfd, &rfds) &&
(bytes = read_eintr(wfd, buf, sizeof(buf))) > 0) {
char *b = buf;
while (bytes) {
int written = BIO_write(socket, b, bytes);
if (!written) {
fprintf(stderr, "BIO_write wrote nothing\n");
return false;
}
if (written < 0) {
if (async) {
AsyncBioAllowWrite(socket, 1);
continue;
}
fprintf(stderr, "BIO_write failed\n");
return false;
}
b += written;
bytes -= written;
}
// Flush all pending data from the handshaker to the client before
// considering control messages.
continue;
}
if (!FD_ISSET(control, &rfds)) {
continue;
}
char msg;
if (read_eintr(control, &msg, 1) != 1) {
perror("read");
return false;
}
switch (msg) {
case kControlMsgDone:
return true;
case kControlMsgError:
return false;
case kControlMsgWantRead:
break;
default:
fprintf(stderr, "Unknown control message from handshaker: %c\n", msg);
return false;
}
auto proxy_data = [&](uint8_t *out, size_t len) -> bool {
if (async) {
AsyncBioAllowRead(socket, len);
}
while (len > 0) {
int bytes_read = BIO_read(socket, out, len);
if (bytes_read < 1) {
fprintf(stderr, "BIO_read failed\n");
return false;
}
ssize_t bytes_written = write_eintr(rfd, out, bytes_read);
if (bytes_written == -1) {
perror("write");
return false;
}
if (bytes_written != bytes_read) {
fprintf(stderr, "short write (%zd of %d bytes)\n", bytes_written,
bytes_read);
return false;
}
len -= bytes_read;
out += bytes_read;
}
return true;
};
// Process one SSL record at a time. That way, we don't send the handshaker
// anything it doesn't want to process, e.g. early data.
uint8_t header[SSL3_RT_HEADER_LENGTH];
if (!proxy_data(header, sizeof(header))) {
return false;
}
if (header[1] != 3) {
fprintf(stderr, "bad header\n");
return false;
}
size_t remaining = (header[3] << 8) + header[4];
while (remaining > 0) {
uint8_t readbuf[64];
size_t len = remaining > sizeof(readbuf) ? sizeof(readbuf) : remaining;
if (!proxy_data(readbuf, len)) {
return false;
}
remaining -= len;
}
// The handshaker blocks on the control channel, so we have to signal
// it that the data have been written.
msg = kControlMsgWriteCompleted;
if (write_eintr(control, &msg, 1) != 1) {
perror("write");
return false;
}
}
}
class ScopedFD {
public:
ScopedFD() : fd_(-1) {}
explicit ScopedFD(int fd) : fd_(fd) {}
~ScopedFD() { Reset(); }
ScopedFD(ScopedFD &&other) { *this = std::move(other); }
ScopedFD &operator=(ScopedFD &&other) {
Reset(other.fd_);
other.fd_ = -1;
return *this;
}
int fd() const { return fd_; }
void Reset(int fd = -1) {
if (fd_ >= 0) {
close(fd_);
}
fd_ = fd;
}
private:
int fd_;
};
class ScopedProcess {
public:
ScopedProcess() : pid_(-1) {}
~ScopedProcess() { Reset(); }
ScopedProcess(ScopedProcess &&other) { *this = std::move(other); }
ScopedProcess &operator=(ScopedProcess &&other) {
Reset(other.pid_);
other.pid_ = -1;
return *this;
}
pid_t pid() const { return pid_; }
void Reset(pid_t pid = -1) {
if (pid_ >= 0) {
kill(pid_, SIGTERM);
int unused;
Wait(&unused);
}
pid_ = pid;
}
bool Wait(int *out_status) {
if (pid_ < 0) {
return false;
}
if (waitpid_eintr(pid_, out_status, 0) != pid_) {
return false;
}
pid_ = -1;
return true;
}
private:
pid_t pid_;
};
class FileActionsDestroyer {
public:
explicit FileActionsDestroyer(posix_spawn_file_actions_t *actions)
: actions_(actions) {}
~FileActionsDestroyer() { posix_spawn_file_actions_destroy(actions_); }
FileActionsDestroyer(const FileActionsDestroyer &) = delete;
FileActionsDestroyer &operator=(const FileActionsDestroyer &) = delete;
private:
posix_spawn_file_actions_t *actions_;
};
// StartHandshaker starts the handshaker process and, on success, returns a
// handle to the process in |*out|. It sets |*out_control| to a control pipe to
// the process. |map_fds| maps from desired fd number in the child process to
// the source fd in the calling process. |close_fds| is the list of additional
// fds to close, which may overlap with |map_fds|. Other than stdin, stdout, and
// stderr, the status of fds not listed in either set is undefined.
static bool StartHandshaker(ScopedProcess *out, ScopedFD *out_control,
const TestConfig *config, bool is_resume,
std::map<int, int> map_fds,
std::vector<int> close_fds) {
if (config->handshaker_path.empty()) {
fprintf(stderr, "no -handshaker-path specified\n");
return false;
}
struct stat dummy;
if (stat(config->handshaker_path.c_str(), &dummy) == -1) {
perror(config->handshaker_path.c_str());
return false;
}
std::vector<const char *> args;
args.push_back(config->handshaker_path.c_str());
static const char kResumeFlag[] = "-handshaker-resume";
if (is_resume) {
args.push_back(kResumeFlag);
}
// config->handshaker_args omits argv[0].
for (const char *arg : config->handshaker_args) {
args.push_back(arg);
}
args.push_back(nullptr);
// A datagram socket guarantees that writes are all-or-nothing.
int control[2];
if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, control) != 0) {
perror("socketpair");
return false;
}
ScopedFD scoped_control0(control[0]), scoped_control1(control[1]);
close_fds.push_back(control[0]);
map_fds[kFdControl] = control[1];
posix_spawn_file_actions_t actions;
if (posix_spawn_file_actions_init(&actions) != 0) {
return false;
}
FileActionsDestroyer actions_destroyer(&actions);
for (int fd : close_fds) {
if (posix_spawn_file_actions_addclose(&actions, fd) != 0) {
return false;
}
}
if (!map_fds.empty()) {
int max_fd = STDERR_FILENO;
for (const auto &pair : map_fds) {
max_fd = std::max(max_fd, pair.first);
max_fd = std::max(max_fd, pair.second);
}
// |map_fds| may contain cycles, so make a copy of all the source fds.
// |posix_spawn| can only use |dup2|, not |dup|, so we assume |max_fd| is
// the last fd we care about inheriting. |temp_fds| maps from fd number in
// the parent process to a temporary fd number in the child process.
std::map<int, int> temp_fds;
int next_fd = max_fd + 1;
for (const auto &pair : map_fds) {
if (temp_fds.count(pair.second)) {
continue;
}
temp_fds[pair.second] = next_fd;
if (posix_spawn_file_actions_adddup2(&actions, pair.second, next_fd) !=
0 ||
posix_spawn_file_actions_addclose(&actions, pair.second) != 0) {
return false;
}
next_fd++;
}
for (const auto &pair : map_fds) {
if (posix_spawn_file_actions_adddup2(&actions, temp_fds[pair.second],
pair.first) != 0) {
return false;
}
}
// Clean up temporary fds.
for (int fd = max_fd + 1; fd < next_fd; fd++) {
if (posix_spawn_file_actions_addclose(&actions, fd) != 0) {
return false;
}
}
}
fflush(stdout);
fflush(stderr);
// MSan doesn't know that |posix_spawn| initializes its output, so initialize
// it to -1.
pid_t pid = -1;
if (posix_spawn(&pid, args[0], &actions, nullptr,
const_cast<char *const *>(args.data()), environ) != 0) {
return false;
}
out->Reset(pid);
*out_control = std::move(scoped_control0);
return true;
}
// RunHandshaker forks and execs the handshaker binary, handing off |input|,
// and, after proxying some amount of handshake traffic, handing back |out|.
static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume,
Span<const uint8_t> input,
std::vector<uint8_t> *out) {
int rfd[2], wfd[2];
// We use pipes, rather than some other mechanism, for their buffers. During
// the handshake, this process acts as a dumb proxy until receiving the
// handback signal, which arrives asynchronously. The race condition means
// that this process could incorrectly proxy post-handshake data from the
// client to the handshaker.
//
// To avoid this, this process never proxies data to the handshaker that the
// handshaker has not explicitly requested as a result of hitting
// |SSL_ERROR_WANT_READ|. Pipes allow the data to sit in a buffer while the
// two processes synchronize over the |control| channel.
if (pipe(rfd) != 0) {
perror("pipe");
return false;
}
ScopedFD rfd0_closer(rfd[0]), rfd1_closer(rfd[1]);
if (pipe(wfd) != 0) {
perror("pipe");
return false;
}
ScopedFD wfd0_closer(wfd[0]), wfd1_closer(wfd[1]);
ScopedProcess handshaker;
ScopedFD control;
if (!StartHandshaker(
&handshaker, &control, config, is_resume,
{{kFdProxyToHandshaker, rfd[0]}, {kFdHandshakerToProxy, wfd[1]}},
{rfd[1], wfd[0]})) {
return false;
}
rfd0_closer.Reset();
wfd1_closer.Reset();
if (write_eintr(control.fd(), input.data(), input.size()) == -1) {
perror("write");
return false;
}
bool ok = Proxy(bio, config->async, control.fd(), rfd[1], wfd[0]);
int wstatus;
if (!handshaker.Wait(&wstatus)) {
perror("waitpid");
return false;
}
if (ok && wstatus) {
fprintf(stderr, "handshaker exited irregularly\n");
return false;
}
if (!ok) {
return false; // This is a "good", i.e. expected, error.
}
constexpr size_t kBufSize = 1024 * 1024;
std::vector<uint8_t> buf(kBufSize);
ssize_t len = read_eintr(control.fd(), buf.data(), buf.size());
if (len == -1) {
perror("read");
return false;
}
buf.resize(len);
*out = std::move(buf);
return true;
}
static bool RequestHandshakeHint(const TestConfig *config, bool is_resume,
Span<const uint8_t> input, bool *out_has_hints,
std::vector<uint8_t> *out_hints) {
ScopedProcess handshaker;
ScopedFD control;
if (!StartHandshaker(&handshaker, &control, config, is_resume, {}, {})) {
return false;
}
if (write_eintr(control.fd(), input.data(), input.size()) == -1) {
perror("write");
return false;
}
char msg;
if (read_eintr(control.fd(), &msg, 1) != 1) {
perror("read");
return false;
}
switch (msg) {
case kControlMsgDone: {
constexpr size_t kBufSize = 1024 * 1024;
out_hints->resize(kBufSize);
ssize_t len =
read_eintr(control.fd(), out_hints->data(), out_hints->size());
if (len == -1) {
perror("read");
return false;
}
out_hints->resize(len);
*out_has_hints = true;
break;
}
case kControlMsgError:
*out_has_hints = false;
break;
default:
fprintf(stderr, "Unknown control message from handshaker: %c\n", msg);
return false;
}
int wstatus;
if (!handshaker.Wait(&wstatus)) {
perror("waitpid");
return false;
}
if (wstatus) {
fprintf(stderr, "handshaker exited irregularly\n");
return false;
}
return true;
}
// PrepareHandoff accepts the |ClientHello| from |ssl| and serializes state to
// be passed to the handshaker. The serialized state includes both the SSL
// handoff, as well test-related state.
static bool PrepareHandoff(SSL *ssl, SettingsWriter *writer,
std::vector<uint8_t> *out_handoff) {
SSL_set_handoff_mode(ssl, 1);
const TestConfig *config = GetTestConfig(ssl);
int ret = -1;
do {
ret = CheckIdempotentError(
"SSL_do_handshake", ssl,
[&]() -> int { return SSL_do_handshake(ssl); });
} while (!HandoffReady(ssl, ret) &&
config->async &&
RetryAsync(ssl, ret));
if (!HandoffReady(ssl, ret)) {
fprintf(stderr, "Handshake failed while waiting for handoff.\n");
return false;
}
ScopedCBB cbb;
SSL_CLIENT_HELLO hello;
if (!CBB_init(cbb.get(), 512) ||
!SSL_serialize_handoff(ssl, cbb.get(), &hello) ||
!writer->WriteHandoff({CBB_data(cbb.get()), CBB_len(cbb.get())}) ||
!SerializeContextState(SSL_get_SSL_CTX(ssl), cbb.get()) ||
!GetTestState(ssl)->Serialize(cbb.get())) {
fprintf(stderr, "Handoff serialisation failed.\n");
return false;
}
out_handoff->assign(CBB_data(cbb.get()),
CBB_data(cbb.get()) + CBB_len(cbb.get()));
return true;
}
// DoSplitHandshake delegates the SSL handshake to a separate process, called
// the handshaker. This process proxies I/O between the handshaker and the
// client, using the |BIO| from |ssl|. After a successful handshake, |ssl| is
// replaced with a new |SSL| object, in a way that is intended to be invisible
// to the caller.
bool DoSplitHandshake(UniquePtr<SSL> *ssl, SettingsWriter *writer,
bool is_resume) {
assert(SSL_get_rbio(ssl->get()) == SSL_get_wbio(ssl->get()));
std::vector<uint8_t> handshaker_input;
const TestConfig *config = GetTestConfig(ssl->get());
// out is the response from the handshaker, which includes a serialized
// handback message, but also serialized updates to the |TestState|.
std::vector<uint8_t> out;
if (!PrepareHandoff(ssl->get(), writer, &handshaker_input) ||
!RunHandshaker(SSL_get_rbio(ssl->get()), config, is_resume,
handshaker_input, &out)) {
fprintf(stderr, "Handoff failed.\n");
return false;
}
SSL_CTX *ctx = SSL_get_SSL_CTX(ssl->get());
UniquePtr<SSL> ssl_handback = config->NewSSL(ctx, nullptr, nullptr);
if (!ssl_handback) {
return false;
}
CBS output, handback;
CBS_init(&output, out.data(), out.size());
if (!CBS_get_u24_length_prefixed(&output, &handback) ||
!DeserializeContextState(&output, ctx) ||
!SetTestState(ssl_handback.get(), TestState::Deserialize(&output, ctx)) ||
!GetTestState(ssl_handback.get()) || !writer->WriteHandback(handback) ||
!SSL_apply_handback(ssl_handback.get(), handback)) {
fprintf(stderr, "Handback failed.\n");
return false;
}
MoveBIOs(ssl_handback.get(), ssl->get());
GetTestState(ssl_handback.get())->async_bio =
GetTestState(ssl->get())->async_bio;
GetTestState(ssl->get())->async_bio = nullptr;
*ssl = std::move(ssl_handback);
return true;
}
bool GetHandshakeHint(SSL *ssl, SettingsWriter *writer, bool is_resume,
const SSL_CLIENT_HELLO *client_hello) {
ScopedCBB input;
CBB child;
if (!CBB_init(input.get(), client_hello->client_hello_len + 256) ||
!CBB_add_u24_length_prefixed(input.get(), &child) ||
!CBB_add_bytes(&child, client_hello->client_hello,
client_hello->client_hello_len) ||
!CBB_add_u24_length_prefixed(input.get(), &child) ||
!SSL_serialize_capabilities(ssl, &child) || //
!CBB_flush(input.get())) {
return false;
}
bool has_hints;
std::vector<uint8_t> hints;
if (!RequestHandshakeHint(
GetTestConfig(ssl), is_resume,
MakeConstSpan(CBB_data(input.get()), CBB_len(input.get())),
&has_hints, &hints)) {
return false;
}
if (has_hints &&
(!writer->WriteHints(hints) ||
!SSL_set_handshake_hints(ssl, hints.data(), hints.size()))) {
return false;
}
return true;
}
#endif // defined(HANDSHAKER_SUPPORTED)

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_TEST_HANDSHAKE
#define HEADER_TEST_HANDSHAKE
#include <functional>
#include <openssl/base.h>
#include "settings_writer.h"
#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
#define HANDSHAKER_SUPPORTED
#endif
// RetryAsync is called after a failed operation on |ssl| with return code
// |ret|. If the operation should be retried, it simulates one asynchronous
// event and returns true. Otherwise it returns false.
bool RetryAsync(SSL *ssl, int ret);
// CheckIdempotentError runs |func|, an operation on |ssl|, ensuring that
// errors are idempotent.
int CheckIdempotentError(const char *name, SSL *ssl, std::function<int()> func);
#if defined(HANDSHAKER_SUPPORTED)
// DoSplitHandshake delegates the SSL handshake to a separate process, called
// the handshaker. This process proxies I/O between the handshaker and the
// client, using the |BIO| from |ssl|. After a successful handshake, |ssl| is
// replaced with a new |SSL| object, in a way that is intended to be invisible
// to the caller.
bool DoSplitHandshake(bssl::UniquePtr<SSL> *ssl, SettingsWriter *writer,
bool is_resume);
// GetHandshakeHint requests a handshake hint from the handshaker process and
// configures the result on |ssl|. It returns true on success and false on
// error.
bool GetHandshakeHint(SSL *ssl, SettingsWriter *writer, bool is_resume,
const SSL_CLIENT_HELLO *client_hello);
// The protocol between the proxy and the handshaker is defined by these
// single-character prefixes. |kControlMsgDone| uses 'H' for compatibility with
// older binaries.
constexpr char kControlMsgWantRead = 'R'; // Handshaker wants data
constexpr char kControlMsgWriteCompleted = 'W'; // Proxy has sent data
constexpr char kControlMsgDone = 'H'; // Proxy should resume control
constexpr char kControlMsgError = 'E'; // Handshaker hit an error
// The protocol between the proxy and handshaker uses these file descriptors.
constexpr int kFdControl = 3; // Bi-directional dgram socket.
constexpr int kFdProxyToHandshaker = 4; // Uni-directional pipe.
constexpr int kFdHandshakerToProxy = 5; // Uni-directional pipe.
#endif // HANDSHAKER_SUPPORTED
#endif // HEADER_TEST_HANDSHAKE

View File

@@ -0,0 +1,267 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <memory>
#include <openssl/bytestring.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include "handshake_util.h"
#include "test_config.h"
#include "test_state.h"
#include "../../crypto/internal.h"
#include "../../crypto/ube/vm_ube_detect.h"
using namespace bssl;
namespace {
ssize_t read_eintr(int fd, void *out, size_t len) {
ssize_t ret;
do {
ret = read(fd, out, len);
} while (ret < 0 && errno == EINTR);
return ret;
}
ssize_t write_eintr(int fd, const void *in, size_t len) {
ssize_t ret;
do {
ret = write(fd, in, len);
} while (ret < 0 && errno == EINTR);
return ret;
}
bool HandbackReady(SSL *ssl, int ret) {
return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK;
}
bool Handshaker(const TestConfig *config, int rfd, int wfd,
Span<const uint8_t> input, int control) {
UniquePtr<SSL_CTX> ctx = config->SetupCtx(/*old_ctx=*/nullptr);
if (!ctx) {
return false;
}
UniquePtr<SSL> ssl =
config->NewSSL(ctx.get(), /*session=*/nullptr, /*test_state=*/nullptr);
if (!ssl) {
fprintf(stderr, "Error creating SSL object in handshaker.\n");
ERR_print_errors_fp(stderr);
return false;
}
// Set |O_NONBLOCK| in order to break out of the loop when we hit
// |SSL_ERROR_WANT_READ|, so that we can send |kControlMsgWantRead| to the
// proxy.
if (fcntl(rfd, F_SETFL, O_NONBLOCK) != 0) {
perror("fcntl");
return false;
}
SSL_set_rfd(ssl.get(), rfd);
SSL_set_wfd(ssl.get(), wfd);
CBS cbs, handoff;
CBS_init(&cbs, input.data(), input.size());
if (!CBS_get_asn1_element(&cbs, &handoff, CBS_ASN1_SEQUENCE) ||
!DeserializeContextState(&cbs, ctx.get()) ||
!SetTestState(ssl.get(), TestState::Deserialize(&cbs, ctx.get())) ||
!GetTestState(ssl.get()) ||
!SSL_apply_handoff(ssl.get(), handoff)) {
fprintf(stderr, "Handoff application failed.\n");
return false;
}
int ret = 0;
for (;;) {
ret = CheckIdempotentError(
"SSL_do_handshake", ssl.get(),
[&]() -> int { return SSL_do_handshake(ssl.get()); });
if (SSL_get_error(ssl.get(), ret) == SSL_ERROR_WANT_READ) {
// Synchronize with the proxy, i.e. don't let the handshake continue until
// the proxy has sent more data.
char msg = kControlMsgWantRead;
if (write_eintr(control, &msg, 1) != 1 ||
read_eintr(control, &msg, 1) != 1 ||
msg != kControlMsgWriteCompleted) {
fprintf(stderr, "read via proxy failed\n");
return false;
}
continue;
}
if (!RetryAsync(ssl.get(), ret)) {
break;
}
}
if (!HandbackReady(ssl.get(), ret)) {
fprintf(stderr, "Handshaker: %s\n",
SSL_error_description(SSL_get_error(ssl.get(), ret)));
ERR_print_errors_fp(stderr);
return false;
}
ScopedCBB output;
CBB handback;
if (!CBB_init(output.get(), 1024) ||
!CBB_add_u24_length_prefixed(output.get(), &handback) ||
!SSL_serialize_handback(ssl.get(), &handback) ||
!SerializeContextState(ctx.get(), output.get()) ||
!GetTestState(ssl.get())->Serialize(output.get())) {
fprintf(stderr, "Handback serialisation failed.\n");
return false;
}
char msg = kControlMsgDone;
if (write_eintr(control, &msg, 1) == -1 ||
write_eintr(control, CBB_data(output.get()), CBB_len(output.get())) ==
-1) {
perror("write");
return false;
}
return true;
}
bool GenerateHandshakeHint(const TestConfig *config,
bssl::Span<const uint8_t> request, int control) {
// The handshake hint contains the ClientHello and the capabilities string.
CBS cbs = request;
CBS client_hello, capabilities;
if (!CBS_get_u24_length_prefixed(&cbs, &client_hello) ||
!CBS_get_u24_length_prefixed(&cbs, &capabilities) || //
CBS_len(&cbs) != 0) {
fprintf(stderr, "Handshaker: Could not parse hint request\n");
return false;
}
UniquePtr<SSL_CTX> ctx = config->SetupCtx(/*old_ctx=*/nullptr);
if (!ctx) {
return false;
}
UniquePtr<SSL> ssl =
config->NewSSL(ctx.get(), /*session=*/nullptr,
std::unique_ptr<TestState>(new TestState));
if (!ssl) {
fprintf(stderr, "Error creating SSL object in handshaker.\n");
ERR_print_errors_fp(stderr);
return false;
}
// TODO(davidben): When split handshakes is replaced, move this into |NewSSL|.
assert(config->is_server);
SSL_set_accept_state(ssl.get());
if (!SSL_request_handshake_hints(
ssl.get(), CBS_data(&client_hello), CBS_len(&client_hello),
CBS_data(&capabilities), CBS_len(&capabilities))) {
fprintf(stderr, "Handshaker: SSL_request_handshake_hints failed\n");
return false;
}
int ret = 0;
do {
ret = CheckIdempotentError("SSL_do_handshake", ssl.get(),
[&] { return SSL_do_handshake(ssl.get()); });
} while (RetryAsync(ssl.get(), ret));
if (ret > 0) {
fprintf(stderr, "Handshaker: handshake unexpectedly succeeded.\n");
return false;
}
if (SSL_get_error(ssl.get(), ret) != SSL_ERROR_HANDSHAKE_HINTS_READY) {
// Errors here may be expected if the test is testing a failing case. The
// shim should continue executing without a hint, so we report an error
// "successfully". This allows the shim to distinguish this from the other
// unexpected error cases.
//
// We intentionally avoid printing the error in this case, to avoid mixing
// up test expectations with errors from the shim.
char msg = kControlMsgError;
if (write_eintr(control, &msg, 1) == -1) {
return false;
}
return true;
}
bssl::ScopedCBB hints;
if (!CBB_init(hints.get(), 256) ||
!SSL_serialize_handshake_hints(ssl.get(), hints.get())) {
fprintf(stderr, "Handshaker: failed to serialize handshake hints\n");
return false;
}
char msg = kControlMsgDone;
if (write_eintr(control, &msg, 1) == -1 ||
write_eintr(control, CBB_data(hints.get()), CBB_len(hints.get())) == -1) {
perror("write");
return false;
}
return true;
}
int SignalError() {
const char msg = kControlMsgError;
if (write_eintr(kFdControl, &msg, 1) != 1) {
return 2;
}
return 1;
}
} // namespace
int main(int argc, char **argv) {
#if defined(OPENSSL_LINUX) && defined(AWSLC_VM_UBE_TESTING)
if (1 != HAZMAT_init_sysgenid_file()) {
abort();
}
#endif
TestConfig initial_config, resume_config, retry_config;
if (!ParseConfig(argc - 1, argv + 1, /*is_shim=*/false, &initial_config,
&resume_config, &retry_config)) {
return SignalError();
}
const TestConfig *config =
initial_config.handshaker_resume ? &resume_config : &initial_config;
#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
if (initial_config.handshaker_resume) {
// If the PRNG returns exactly the same values when trying to resume then a
// "random" session ID will happen to exactly match the session ID
// "randomly" generated on the initial connection. The client will thus
// incorrectly believe that the server is resuming.
uint8_t byte;
RAND_bytes(&byte, 1);
}
#endif // BORINGSSL_UNSAFE_DETERMINISTIC_MODE
// read() will return the entire message in one go, because it's a datagram
// socket.
constexpr size_t kBufSize = 1024 * 1024;
std::vector<uint8_t> request(kBufSize);
ssize_t len = read_eintr(kFdControl, request.data(), request.size());
if (len == -1) {
perror("read");
return 2;
}
request.resize(static_cast<size_t>(len));
if (config->handshake_hints) {
if (!GenerateHandshakeHint(config, request, kFdControl)) {
return SignalError();
}
} else {
if (!Handshaker(config, kFdProxyToHandshaker, kFdHandshakerToProxy,
request, kFdControl)) {
return SignalError();
}
}
return 0;
}

View File

@@ -0,0 +1,267 @@
// Copyright (c) 2019, Google Inc.
// SPDX-License-Identifier: ISC
#include "mock_quic_transport.h"
#include <openssl/span.h>
#include <algorithm>
#include <climits>
#include <cstring>
MockQuicTransport::MockQuicTransport(bssl::UniquePtr<BIO> bio, SSL *ssl)
: bio_(std::move(bio)),
read_levels_(ssl_encryption_application + 1),
write_levels_(ssl_encryption_application + 1),
ssl_(ssl) {}
bool MockQuicTransport::SetReadSecret(enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher,
const uint8_t *secret,
size_t secret_len) {
// TODO(davidben): Assert the various encryption secret invariants.
read_levels_[level].cipher = SSL_CIPHER_get_protocol_id(cipher);
read_levels_[level].secret.assign(secret, secret + secret_len);
return true;
}
bool MockQuicTransport::SetWriteSecret(enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher,
const uint8_t *secret,
size_t secret_len) {
// TODO(davidben): Assert the various encryption secret invariants.
write_levels_[level].cipher = SSL_CIPHER_get_protocol_id(cipher);
write_levels_[level].secret.assign(secret, secret + secret_len);
return true;
}
namespace {
bool ReadAll(BIO *bio, bssl::Span<uint8_t> out) {
size_t len = out.size();
uint8_t *buf = out.data();
while (len > 0) {
size_t chunk_len = std::min(len, size_t{INT_MAX});
int ret = BIO_read(bio, buf, static_cast<int>(chunk_len));
if (ret <= 0) {
return false;
}
buf += ret;
len -= ret;
}
return true;
}
const char *LevelToString(ssl_encryption_level_t level) {
switch (level) {
case ssl_encryption_initial:
return "initial";
case ssl_encryption_early_data:
return "early_data";
case ssl_encryption_handshake:
return "handshake";
case ssl_encryption_application:
return "application";
}
return "";
}
} // namespace
bool MockQuicTransport::ReadHeader(uint8_t *out_type,
enum ssl_encryption_level_t *out_level,
size_t *out_len) {
for (;;) {
uint8_t header[8];
if (!ReadAll(bio_.get(), header)) {
// TODO(davidben): Distinguish between errors and EOF. See
// ReadApplicationData.
return false;
}
CBS cbs;
uint8_t level_id;
uint16_t cipher_suite;
uint32_t remaining_bytes;
CBS_init(&cbs, header, sizeof(header));
if (!CBS_get_u8(&cbs, out_type) ||
!CBS_get_u8(&cbs, &level_id) ||
!CBS_get_u16(&cbs, &cipher_suite) ||
!CBS_get_u32(&cbs, &remaining_bytes) ||
level_id >= read_levels_.size()) {
fprintf(stderr, "Error parsing record header.\n");
return false;
}
auto level = static_cast<ssl_encryption_level_t>(level_id);
// Non-initial levels must be configured before use.
uint16_t expect_cipher = read_levels_[level].cipher;
if (expect_cipher == 0 && level != ssl_encryption_initial) {
if (level == ssl_encryption_early_data) {
// If we receive early data records without any early data keys, skip
// the record. This means early data was rejected.
std::vector<uint8_t> discard(remaining_bytes);
if (!ReadAll(bio_.get(), bssl::MakeSpan(discard))) {
return false;
}
continue;
}
fprintf(stderr,
"Got record at level %s, but keys were not configured.\n",
LevelToString(level));
return false;
}
if (cipher_suite != expect_cipher) {
fprintf(stderr, "Got cipher suite 0x%04x at level %s, wanted 0x%04x.\n",
cipher_suite, LevelToString(level), expect_cipher);
return false;
}
const std::vector<uint8_t> &secret = read_levels_[level].secret;
std::vector<uint8_t> read_secret(secret.size());
if (remaining_bytes < secret.size()) {
fprintf(stderr, "Record at level %s too small.\n", LevelToString(level));
return false;
}
remaining_bytes -= secret.size();
if (!ReadAll(bio_.get(), bssl::MakeSpan(read_secret))) {
fprintf(stderr, "Error reading record secret.\n");
return false;
}
if (read_secret != secret) {
fprintf(stderr, "Encryption secret at level %s did not match.\n",
LevelToString(level));
return false;
}
*out_level = level;
*out_len = remaining_bytes;
return true;
}
}
bool MockQuicTransport::ReadHandshake() {
uint8_t type;
ssl_encryption_level_t level;
size_t len;
if (!ReadHeader(&type, &level, &len)) {
return false;
}
if (type != SSL3_RT_HANDSHAKE) {
return false;
}
std::vector<uint8_t> buf(len);
if (!ReadAll(bio_.get(), bssl::MakeSpan(buf))) {
return false;
}
return SSL_provide_quic_data(ssl_, level, buf.data(), buf.size());
}
int MockQuicTransport::ReadApplicationData(uint8_t *out, size_t max_out) {
if (pending_app_data_.size() > 0) {
size_t len = pending_app_data_.size() - app_data_offset_;
if (len > max_out) {
len = max_out;
}
memcpy(out, pending_app_data_.data() + app_data_offset_, len);
app_data_offset_ += len;
if (app_data_offset_ == pending_app_data_.size()) {
pending_app_data_.clear();
app_data_offset_ = 0;
}
return len;
}
uint8_t type = 0;
ssl_encryption_level_t level;
size_t len;
while (true) {
if (!ReadHeader(&type, &level, &len)) {
// Assume that a failure to read the header means there's no more to read,
// not an error reading.
return 0;
}
if (type == SSL3_RT_APPLICATION_DATA) {
break;
}
if (type != SSL3_RT_HANDSHAKE) {
return -1;
}
std::vector<uint8_t> buf(len);
if (!ReadAll(bio_.get(), bssl::MakeSpan(buf))) {
return -1;
}
if (SSL_provide_quic_data(ssl_, level, buf.data(), buf.size()) != 1) {
return -1;
}
if (SSL_in_init(ssl_)) {
int ret = SSL_do_handshake(ssl_);
if (ret < 0) {
int ssl_err = SSL_get_error(ssl_, ret);
if (ssl_err == SSL_ERROR_WANT_READ) {
continue;
}
return -1;
}
} else if (SSL_process_quic_post_handshake(ssl_) != 1) {
return -1;
}
}
uint8_t *buf = out;
if (len > max_out) {
pending_app_data_.resize(len);
buf = pending_app_data_.data();
}
app_data_offset_ = 0;
if (!ReadAll(bio_.get(), bssl::MakeSpan(buf, len))) {
return -1;
}
if (len > max_out) {
memcpy(out, buf, max_out);
app_data_offset_ = max_out;
return max_out;
}
return len;
}
bool MockQuicTransport::WriteRecord(enum ssl_encryption_level_t level,
uint8_t type, const uint8_t *data,
size_t len) {
uint16_t cipher_suite = write_levels_[level].cipher;
const std::vector<uint8_t> &secret = write_levels_[level].secret;
size_t tlv_len = secret.size() + len;
uint8_t header[8];
header[0] = type;
header[1] = level;
header[2] = (cipher_suite >> 8) & 0xff;
header[3] = cipher_suite & 0xff;
header[4] = (tlv_len >> 24) & 0xff;
header[5] = (tlv_len >> 16) & 0xff;
header[6] = (tlv_len >> 8) & 0xff;
header[7] = tlv_len & 0xff;
return BIO_write_all(bio_.get(), header, sizeof(header)) &&
BIO_write_all(bio_.get(), secret.data(), secret.size()) &&
BIO_write_all(bio_.get(), data, len);
}
bool MockQuicTransport::WriteHandshakeData(enum ssl_encryption_level_t level,
const uint8_t *data, size_t len) {
return WriteRecord(level, SSL3_RT_HANDSHAKE, data, len);
}
bool MockQuicTransport::WriteApplicationData(const uint8_t *in, size_t len) {
enum ssl_encryption_level_t level = ssl_encryption_application;
if (SSL_in_early_data(ssl_) && !SSL_is_server(ssl_)) {
level = ssl_encryption_early_data;
}
return WriteRecord(level, SSL3_RT_APPLICATION_DATA, in, len);
}
bool MockQuicTransport::Flush() { return BIO_flush(bio_.get()) > 0; }
bool MockQuicTransport::SendAlert(enum ssl_encryption_level_t level,
uint8_t alert) {
uint8_t alert_msg[] = {2, alert};
return WriteRecord(level, SSL3_RT_ALERT, alert_msg, sizeof(alert_msg));
}

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2019, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_MOCK_QUIC_TRANSPORT
#define HEADER_MOCK_QUIC_TRANSPORT
#include <openssl/base.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <vector>
class MockQuicTransport {
public:
explicit MockQuicTransport(bssl::UniquePtr<BIO> bio, SSL *ssl);
bool SetReadSecret(enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secret_len);
bool SetWriteSecret(enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secret_len);
bool ReadHandshake();
bool WriteHandshakeData(enum ssl_encryption_level_t level,
const uint8_t *data, size_t len);
// Returns the number of bytes read.
int ReadApplicationData(uint8_t *out, size_t max_out);
bool WriteApplicationData(const uint8_t *in, size_t len);
bool Flush();
bool SendAlert(enum ssl_encryption_level_t level, uint8_t alert);
private:
// Reads a record header from |bio_| and returns whether the record was read
// successfully. As part of reading the header, this function checks that the
// cipher suite and secret in the header are correct. On success, the TLS
// record type is put in |*out_type|, the encryption level is put in
// |*out_level|, the length of the TLS record is put in |*out_len|, and the
// next thing to be read from |bio_| is |*out_len| bytes of the TLS record.
bool ReadHeader(uint8_t *out_type, enum ssl_encryption_level_t *out_level,
size_t *out_len);
// Writes a MockQuicTransport record to |bio_| at encryption level |level|
// with record type |type| and a TLS record payload of length |len| from
// |data|.
bool WriteRecord(enum ssl_encryption_level_t level, uint8_t type,
const uint8_t *data, size_t len);
bssl::UniquePtr<BIO> bio_;
std::vector<uint8_t> pending_app_data_;
size_t app_data_offset_;
struct EncryptionLevel {
uint16_t cipher;
std::vector<uint8_t> secret;
};
std::vector<EncryptionLevel> read_levels_;
std::vector<EncryptionLevel> write_levels_;
SSL *ssl_; // Unowned.
};
#endif // HEADER_MOCK_QUIC_TRANSPORT

View File

@@ -0,0 +1,254 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#include "packeted_bio.h"
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <openssl/mem.h>
#include "../../crypto/internal.h"
namespace {
extern const BIO_METHOD g_packeted_bio_method;
const uint8_t kOpcodePacket = 'P';
const uint8_t kOpcodeTimeout = 'T';
const uint8_t kOpcodeTimeoutAck = 't';
struct PacketedBio {
explicit PacketedBio(timeval *clock_arg)
: clock(clock_arg) {
OPENSSL_memset(&timeout, 0, sizeof(timeout));
}
bool HasTimeout() const {
return timeout.tv_sec != 0 || timeout.tv_usec != 0;
}
timeval timeout;
timeval *clock;
};
PacketedBio *GetData(BIO *bio) {
if (bio->method != &g_packeted_bio_method) {
return NULL;
}
return (PacketedBio *)bio->ptr;
}
// ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
// 0 or -1 on error.
static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
while (len > 0) {
int chunk_len = INT_MAX;
if (len <= INT_MAX) {
chunk_len = (int)len;
}
int ret = BIO_read(bio, out, chunk_len);
if (ret <= 0) {
return ret;
}
out += ret;
len -= ret;
}
return 1;
}
static int PacketedWrite(BIO *bio, const char *in, int inl) {
if (bio->next_bio == NULL) {
return 0;
}
BIO_clear_retry_flags(bio);
// Write the header.
uint8_t header[5];
header[0] = kOpcodePacket;
header[1] = (inl >> 24) & 0xff;
header[2] = (inl >> 16) & 0xff;
header[3] = (inl >> 8) & 0xff;
header[4] = inl & 0xff;
int ret = BIO_write(bio->next_bio, header, sizeof(header));
if (ret <= 0) {
BIO_copy_next_retry(bio);
return ret;
}
// Write the buffer.
ret = BIO_write(bio->next_bio, in, inl);
if (ret < 0 || (inl > 0 && ret == 0)) {
BIO_copy_next_retry(bio);
return ret;
}
assert(ret == inl);
return ret;
}
static int PacketedRead(BIO *bio, char *out, int outl) {
PacketedBio *data = GetData(bio);
if (bio->next_bio == NULL) {
return 0;
}
BIO_clear_retry_flags(bio);
// Read the opcode.
uint8_t opcode;
int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode));
if (ret <= 0) {
BIO_copy_next_retry(bio);
return ret;
}
if (opcode == kOpcodeTimeout) {
// The caller is required to advance any pending timeouts before continuing.
if (data->HasTimeout()) {
fprintf(stderr, "Unprocessed timeout!\n");
return -1;
}
// Process the timeout.
uint8_t buf[8];
ret = ReadAll(bio->next_bio, buf, sizeof(buf));
if (ret <= 0) {
BIO_copy_next_retry(bio);
return ret;
}
uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) |
(static_cast<uint64_t>(buf[1]) << 48) |
(static_cast<uint64_t>(buf[2]) << 40) |
(static_cast<uint64_t>(buf[3]) << 32) |
(static_cast<uint64_t>(buf[4]) << 24) |
(static_cast<uint64_t>(buf[5]) << 16) |
(static_cast<uint64_t>(buf[6]) << 8) |
static_cast<uint64_t>(buf[7]);
timeout /= 1000; // Convert nanoseconds to microseconds.
data->timeout.tv_usec = timeout % 1000000;
data->timeout.tv_sec = timeout / 1000000;
// Send an ACK to the peer.
ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1);
if (ret <= 0) {
return ret;
}
assert(ret == 1);
// Signal to the caller to retry the read, after advancing the clock.
BIO_set_retry_read(bio);
return -1;
}
if (opcode != kOpcodePacket) {
fprintf(stderr, "Unknown opcode, %u\n", opcode);
return -1;
}
// Read the length prefix.
uint8_t len_bytes[4];
ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes));
if (ret <= 0) {
BIO_copy_next_retry(bio);
return ret;
}
uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) |
(len_bytes[2] << 8) | len_bytes[3];
uint8_t *buf = (uint8_t *)OPENSSL_malloc(len);
if (buf == NULL) {
return -1;
}
ret = ReadAll(bio->next_bio, buf, len);
if (ret <= 0) {
fprintf(stderr, "Packeted BIO was truncated\n");
return -1;
}
if (outl > (int)len) {
outl = len;
}
OPENSSL_memcpy(out, buf, outl);
OPENSSL_free(buf);
return outl;
}
static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
if (bio->next_bio == NULL) {
return 0;
}
BIO_clear_retry_flags(bio);
long ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
BIO_copy_next_retry(bio);
return ret;
}
static int PacketedNew(BIO *bio) {
bio->init = 1;
return 1;
}
static int PacketedFree(BIO *bio) {
if (bio == NULL) {
return 0;
}
delete GetData(bio);
bio->init = 0;
return 1;
}
static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
if (bio->next_bio == NULL) {
return 0;
}
return BIO_callback_ctrl(bio->next_bio, cmd, fp);
}
const BIO_METHOD g_packeted_bio_method = {
BIO_TYPE_FILTER,
"packeted bio",
PacketedWrite,
PacketedRead,
NULL /* puts */,
NULL /* gets */,
PacketedCtrl,
PacketedNew,
PacketedFree,
PacketedCallbackCtrl,
};
} // namespace
bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock) {
bssl::UniquePtr<BIO> bio(BIO_new(&g_packeted_bio_method));
if (!bio) {
return nullptr;
}
bio->ptr = new PacketedBio(clock);
return bio;
}
bool PacketedBioAdvanceClock(BIO *bio) {
PacketedBio *data = GetData(bio);
if (data == nullptr) {
return false;
}
if (!data->HasTimeout()) {
return false;
}
data->clock->tv_usec += data->timeout.tv_usec;
data->clock->tv_sec += data->clock->tv_usec / 1000000;
data->clock->tv_usec %= 1000000;
data->clock->tv_sec += data->timeout.tv_sec;
OPENSSL_memset(&data->timeout, 0, sizeof(data->timeout));
return true;
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_PACKETED_BIO
#define HEADER_PACKETED_BIO
#include <openssl/base.h>
#include <openssl/bio.h>
#if defined(OPENSSL_WINDOWS)
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <winsock2.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
#else
#include <sys/time.h>
#endif
// PacketedBioCreate creates a filter BIO which implements a reliable in-order
// blocking datagram socket. It uses the value of |*clock| as the clock.
//
// During a |BIO_read|, the peer may signal the filter BIO to simulate a
// timeout. The operation will fail immediately. The caller must then call
// |PacketedBioAdvanceClock| before retrying |BIO_read|.
bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock);
// PacketedBioAdvanceClock advances |bio|'s clock and returns true if there is a
// pending timeout. Otherwise, it returns false.
bool PacketedBioAdvanceClock(BIO *bio);
#endif // HEADER_PACKETED_BIO

View File

@@ -0,0 +1,104 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#include "settings_writer.h"
#include <stdio.h>
#include <openssl/ssl.h>
#include "fuzzer_tags.h"
#include "test_config.h"
SettingsWriter::SettingsWriter() {}
bool SettingsWriter::Init(int i, const TestConfig *config,
SSL_SESSION *session) {
if (config->write_settings.empty()) {
return true;
}
// Treat write_settings as a path prefix for each connection in the run.
char buf[DECIMAL_SIZE(int)];
snprintf(buf, sizeof(buf), "%d", i);
path_ = config->write_settings + buf;
if (!CBB_init(cbb_.get(), 64)) {
return false;
}
if (session != nullptr) {
uint8_t *data;
size_t len;
if (!SSL_SESSION_to_bytes(session, &data, &len)) {
return false;
}
bssl::UniquePtr<uint8_t> free_data(data);
CBB child;
if (!CBB_add_u16(cbb_.get(), kSessionTag) ||
!CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
!CBB_add_bytes(&child, data, len) || !CBB_flush(cbb_.get())) {
return false;
}
}
if (config->is_server &&
(config->require_any_client_certificate || config->verify_peer) &&
!CBB_add_u16(cbb_.get(), kRequestClientCert)) {
return false;
}
return true;
}
bool SettingsWriter::Commit() {
if (path_.empty()) {
return true;
}
uint8_t *settings;
size_t settings_len;
if (!CBB_add_u16(cbb_.get(), kDataTag) ||
!CBB_finish(cbb_.get(), &settings, &settings_len)) {
return false;
}
bssl::UniquePtr<uint8_t> free_settings(settings);
struct FileCloser {
void operator()(FILE *f) const { fclose(f); }
};
using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
ScopedFILE file(fopen(path_.c_str(), "w"));
if (!file) {
return false;
}
return fwrite(settings, settings_len, 1, file.get()) == 1;
}
bool SettingsWriter::WriteHandoff(bssl::Span<const uint8_t> handoff) {
return WriteData(kHandoffTag, handoff);
}
bool SettingsWriter::WriteHandback(bssl::Span<const uint8_t> handback) {
return WriteData(kHandbackTag, handback);
}
bool SettingsWriter::WriteHints(bssl::Span<const uint8_t> hints) {
return WriteData(kHintsTag, hints);
}
bool SettingsWriter::WriteData(uint16_t tag, bssl::Span<const uint8_t> data) {
if (path_.empty()) {
return true;
}
CBB child;
if (!CBB_add_u16(cbb_.get(), tag) ||
!CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
!CBB_add_bytes(&child, data.data(), data.size()) ||
!CBB_flush(cbb_.get())) {
return false;
}
return true;
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_SETTINGS_WRITER
#define HEADER_SETTINGS_WRITER
#include <string>
#include <openssl/bytestring.h>
#include <openssl/ssl.h>
#include "test_config.h"
struct SettingsWriter {
public:
SettingsWriter();
// Init initializes the writer for a new connection, given by |i|. Each
// connection gets a unique output file.
bool Init(int i, const TestConfig *config, SSL_SESSION *session);
// Commit writes the buffered data to disk.
bool Commit();
bool WriteHandoff(bssl::Span<const uint8_t> handoff);
bool WriteHandback(bssl::Span<const uint8_t> handback);
bool WriteHints(bssl::Span<const uint8_t> hints);
private:
bool WriteData(uint16_t tag, bssl::Span<const uint8_t> data);
std::string path_;
bssl::ScopedCBB cbb_;
};
#endif // HEADER_SETTINGS_WRITER

View File

@@ -0,0 +1,171 @@
/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0 OR ISC
*/
#include "ssl_transfer.h"
#include <stdio.h>
#include <time.h>
#include <openssl/ssl.h>
#include "test_config.h"
#include "../internal.h"
SSLTransfer::SSLTransfer() {}
// WriteData writes |input| to disk when |prefix| is not empty.
// This is to generate data to seed related Fuzz corpus generation.
static bool WriteData(std::string prefix, const uint8_t *input, size_t len) {
if (prefix.empty()) {
// WriteData is not needed because related config is not enabled.
return true;
}
struct FileCloser {
void operator()(FILE *f) const { fclose(f); }
};
using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
std::string path = prefix + "-" + std::to_string(rand());
ScopedFILE file(fopen(path.c_str(), "w"));
if (!file) {
return false;
}
return fwrite(input, len, 1, file.get()) == 1;
}
// EncodeAndDecodeSSL transfers the states of |in| to a newly allocated SSL
// by using |SSL_to/from_bytes|. When success, |in| is freed and |out| holds
// the transferred SSL.
static bool EncodeAndDecodeSSL(const TestConfig *config, SSL *in, SSL_CTX *ctx,
bssl::UniquePtr<SSL> *out) {
// Encoding SSL to bytes.
size_t encoded_len;
bssl::UniquePtr<uint8_t> encoded;
uint8_t *encoded_raw;
if (!SSL_to_bytes(in, &encoded_raw, &encoded_len)) {
fprintf(stderr, "SSL_to_bytes failed. Error code: %s\n",
ERR_reason_error_string(ERR_peek_last_error()));
return false;
}
encoded.reset(encoded_raw);
// Decoding SSL from the bytes.
std::string path_prefix = config->ssl_fuzz_seed_path_prefix;
if (!WriteData(path_prefix, encoded.get(), encoded_len)) {
fprintf(stderr, "Failed to write the output of |SSL_to_bytes|.\n");
return false;
}
const uint8_t *ptr2 = encoded.get();
SSL *server2_ = SSL_from_bytes(ptr2, encoded_len, ctx);
if (server2_ == nullptr) {
fprintf(stderr, "SSL_from_bytes failed. Error code: %s\n", ERR_reason_error_string(ERR_peek_last_error()));
return false;
}
out->reset(server2_);
return true;
}
// MoveBIOs moves the |BIO|s of |src| to |dst|.
static void MoveBIOs(SSL *dest, SSL *src) {
BIO *rbio = SSL_get_rbio(src);
BIO_up_ref(rbio);
SSL_set0_rbio(dest, rbio);
BIO *wbio = SSL_get_wbio(src);
BIO_up_ref(wbio);
SSL_set0_wbio(dest, wbio);
SSL_set0_rbio(src, nullptr);
SSL_set0_wbio(src, nullptr);
}
// TransferSSL transfers |in| to |out|.
static bool TransferSSL(const TestConfig *config, bssl::UniquePtr<SSL> *in, bssl::UniquePtr<SSL> *out) {
if (!in || !in->get()) {
return false;
}
SSL_CTX *in_ctx = SSL_get_SSL_CTX(in->get());
// Encode the SSL |in| into bytes.
// Decode the bytes into a new SSL.
bssl::UniquePtr<SSL> decoded_ssl;
if (!EncodeAndDecodeSSL(config, in->get(), in_ctx, &decoded_ssl)){
return false;
}
// Move the bio.
MoveBIOs(decoded_ssl.get(), in->get());
if (!SetTestConfig(decoded_ssl.get(), GetTestConfig(in->get()))) {
return false;
}
// Move the test state.
std::unique_ptr<TestState> state(GetTestState(in->get()));
if (!SetTestState(decoded_ssl.get(), std::move(state))) {
return false;
}
// Unset the test state of |in|.
std::unique_ptr<TestState> tmp1;
if (!SetTestState(in->get(), std::move(tmp1)) || !SetTestConfig(in->get(), nullptr)) {
return false;
}
// Free the SSL of |in|.
SSL_free(in->release());
// If |out| is not nullptr, |out| will hold the decoded SSL.
// Else, |in| will get reset to hold the decoded SSL.
if (out == nullptr) {
in->reset(decoded_ssl.release());
} else {
out->reset(decoded_ssl.release());
}
return true;
}
void SSLTransfer::MarkTest(const TestConfig *config, const SSL *ssl) {
if (config->check_ssl_transfer && IsSupported(ssl)) {
// Below message is to inform runner.go that this test case can
// be converted to test SSL transfer.
// In the converted test, |IsSupported| should be called again
// before |TransferSSL| because each test case may perform
// multiple connections. Not all connections can be transferred.
fprintf(stderr, "Eligible for testing SSL transfer.\n");
}
}
bool SSLTransfer::ResetSSL(const TestConfig *config, bssl::UniquePtr<SSL> *in) {
if (config->do_ssl_transfer && IsSupported(in->get())) {
// Below message is to inform runner.go that this test case
// is going to test SSL transfer.
fprintf(stderr, "SSL transfer is going to be tested.\n");
if (!TransferSSL(config, in, nullptr)) {
return false;
}
}
return true;
}
// IsSupported is wrapper of |ssl_transfer_supported| and includes
// some logics to clean error code that may get generated when not supported.
bool SSLTransfer::IsSupported(const SSL *in) {
// |ssl_transfer_supported| may generate new error code.
// |ERR_set_mark| and |ERR_pop_to_mark| are used to clean the error states.
ERR_set_mark();
bool ret = true;
if (!bssl::ssl_transfer_supported(in)) {
ret = false;
ERR_pop_to_mark();
}
return ret;
}
bool SSLTransfer::MarkOrReset(const TestConfig *config, bssl::UniquePtr<SSL> *in) {
if (!config || !in) {
return false;
}
bool supported = IsSupported(in->get());
if (!supported) {
// Return true because not all ssl connections can be transferred.
return true;
}
MarkTest(config, in->get());
return ResetSSL(config, in);
}

View File

@@ -0,0 +1,33 @@
/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0 OR ISC
*/
#ifndef HEADER_SSL_TRANSFER
#define HEADER_SSL_TRANSFER
#include <string>
#include <openssl/ssl.h>
#include "test_config.h"
struct SSLTransfer {
public:
SSLTransfer();
// MarkTest generate a mark(err msg) if a test case of runner.go can be used to test
// SSL transfer when |config->check_ssl_transfer| is true.
void MarkTest(const TestConfig *config, const SSL *ssl);
// ResetSSL resets |in| with a newly allocated SSL when |config->do_ssl_transfer| is true.
// The newly allocated SSL has states transferred from the previous one hold by |in|.
bool ResetSSL(const TestConfig *config, bssl::UniquePtr<SSL> *in);
// MarkOrReset wraps |MarkTest| and |ResetSSL|.
bool MarkOrReset(const TestConfig *config, bssl::UniquePtr<SSL> *in);
// IsSupported returns true when |in| can be transferred. Otherwise, returns false.
bool IsSupported(const SSL *in);
};
#endif // HEADER_SSL_TRANSFER

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,234 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_TEST_CONFIG
#define HEADER_TEST_CONFIG
#include <string>
#include <utility>
#include <vector>
#include <openssl/base.h>
#include <openssl/x509.h>
#include "test_state.h"
struct TestConfig {
int port = 0;
bool ipv6 = false;
uint64_t shim_id = 0;
bool is_server = false;
bool is_dtls = false;
bool is_quic = false;
int resume_count = 0;
std::string write_settings;
bool fallback_scsv = false;
std::vector<uint16_t> signing_prefs;
std::vector<uint16_t> verify_prefs;
std::vector<uint16_t> expect_peer_verify_prefs;
std::vector<int> curves;
std::string key_file;
std::string cert_file;
std::string trust_cert;
std::string expect_server_name;
bool enable_ech_grease = false;
std::vector<std::string> ech_server_configs;
std::vector<std::string> ech_server_keys;
std::vector<int> ech_is_retry_config;
bool expect_ech_accept = false;
std::string expect_ech_name_override;
bool expect_no_ech_name_override = false;
std::string expect_ech_retry_configs;
bool expect_no_ech_retry_configs = false;
std::string ech_config_list;
std::string expect_certificate_types;
bool require_any_client_certificate = false;
std::string advertise_npn;
std::string expect_next_proto;
bool false_start = false;
std::string select_next_proto;
bool async = false;
bool write_different_record_sizes = false;
bool cbc_record_splitting = false;
bool partial_write = false;
bool no_tls13 = false;
bool no_tls12 = false;
bool no_tls11 = false;
bool no_tls1 = false;
bool no_ticket = false;
std::string expect_channel_id;
bool enable_channel_id = false;
std::string send_channel_id;
bool shim_writes_first = false;
std::string host_name;
std::string advertise_alpn;
std::string expect_alpn;
std::string expect_advertised_alpn;
std::string select_alpn;
bool decline_alpn = false;
bool reject_alpn = false;
bool select_empty_alpn = false;
bool defer_alps = false;
std::vector<std::pair<std::string, std::string>> application_settings;
std::unique_ptr<std::string> expect_peer_application_settings;
bool alps_use_new_codepoint = false;
std::string quic_transport_params;
std::string expect_quic_transport_params;
// Set quic_use_legacy_codepoint to 0 or 1 to configure, -1 uses default.
int quic_use_legacy_codepoint = -1;
bool expect_session_miss = false;
bool expect_extended_master_secret = false;
std::string psk;
std::string psk_identity;
std::string srtp_profiles;
bool enable_ocsp_stapling = false;
std::string expect_ocsp_response;
bool enable_signed_cert_timestamps = false;
std::string expect_signed_cert_timestamps;
uint16_t min_version = 0;
uint16_t max_version = 0;
uint16_t expect_version = 0;
int mtu = 0;
bool implicit_handshake = false;
bool use_early_callback = false;
bool fail_early_callback = false;
bool install_ddos_callback = false;
bool fail_ddos_callback = false;
bool fail_cert_callback = false;
std::string cipher;
bool handshake_never_done = false;
int export_keying_material = 0;
std::string export_label;
std::string export_context;
bool use_export_context = false;
bool tls_unique = false;
bool expect_ticket_renewal = false;
bool expect_no_session = false;
bool expect_ticket_supports_early_data = false;
bool expect_accept_early_data = false;
bool expect_reject_early_data = false;
bool expect_no_offer_early_data = false;
bool use_ticket_callback = false;
bool renew_ticket = false;
bool enable_early_data = false;
bool enable_client_custom_extension = false;
bool enable_server_custom_extension = false;
bool custom_extension_skip = false;
bool custom_extension_fail_add = false;
std::string ocsp_response;
bool check_close_notify = false;
bool shim_shuts_down = false;
bool verify_fail = false;
bool verify_peer = false;
bool verify_peer_if_no_obc = false;
bool expect_verify_result = false;
std::string signed_cert_timestamps;
int expect_total_renegotiations = 0;
bool renegotiate_once = false;
bool renegotiate_freely = false;
bool renegotiate_ignore = false;
bool renegotiate_explicit = false;
bool forbid_renegotiation_after_handshake = false;
uint16_t expect_peer_signature_algorithm = 0;
uint16_t expect_curve_id = 0;
bool use_old_client_cert_callback = false;
int initial_timeout_duration_ms = 0;
std::string use_client_ca_list;
std::string expect_client_ca_list;
bool send_alert = false;
bool peek_then_read = false;
bool enable_grease = false;
bool permute_extensions = false;
int max_cert_list = 0;
std::string ticket_key;
bool use_exporter_between_reads = false;
uint16_t expect_cipher_aes = 0;
uint16_t expect_cipher_no_aes = 0;
uint16_t expect_cipher = 0;
std::string expect_peer_cert_file;
int resumption_delay = 0;
bool retain_only_sha256_client_cert = false;
bool expect_sha256_client_cert = false;
bool read_with_unfinished_write = false;
bool expect_secure_renegotiation = false;
bool expect_no_secure_renegotiation = false;
int max_send_fragment = 0;
int read_size = 0;
bool expect_session_id = false;
bool expect_no_session_id = false;
int expect_ticket_age_skew = 0;
bool no_op_extra_handshake = false;
bool handshake_twice = false;
bool allow_unknown_alpn_protos = false;
bool use_custom_verify_callback = false;
std::string expect_msg_callback;
bool allow_false_start_without_alpn = false;
bool handoff = false;
bool handshake_hints = false;
bool allow_hint_mismatch = false;
bool use_ocsp_callback = false;
bool set_ocsp_in_callback = false;
bool decline_ocsp_callback = false;
bool fail_ocsp_callback = false;
bool install_cert_compression_algs = false;
int install_one_cert_compression_alg = 0;
bool reverify_on_resume = false;
bool enforce_rsa_key_usage = false;
bool expect_key_usage_invalid = false;
bool is_handshaker_supported = false;
bool handshaker_resume = false;
std::string handshaker_path;
bool jdk11_workaround = false;
bool server_preference = false;
bool export_traffic_secrets = false;
bool key_update = false;
bool expect_delegated_credential_used = false;
std::string delegated_credential;
std::string expect_early_data_reason;
bool expect_hrr = false;
bool expect_no_hrr = false;
bool wait_for_debugger = false;
std::string quic_early_data_context;
int early_write_after_message = 0;
// When check_ssl_transfer is true, bssl checks if the ssl can be transferred.
bool check_ssl_transfer = false;
// when do_ssl_transfer is false, no transfer will happen.
// when do_ssl_transfer is true, transfer will happen if the ssl is server.
bool do_ssl_transfer = false;
// When not zero this enables read ahead and sets the buffer to this size.
int read_ahead_buffer_size = 0;
// When not empty, this prefix with random suffix is used to create a file
// stores the output of |SSL_to_bytes|.
std::string ssl_fuzz_seed_path_prefix;
// When not empty, the value is passed to |SSL_CTX_set_ciphersuites|.
std::string tls13_ciphersuites;
// multiple_certs_slot is used to associate the server with the multiple
// certificate/private key slot configuration. The certificate comes first,
// then the private key.
// When |multiple_certs_slot| is defined, the certificates defined are
// prioritized over certs defined with |cert_file| and |key_file|.
std::vector<std::pair<std::string, std::string>> multiple_certs_slot;
bool no_check_client_certificate_type = false;
std::vector<const char*> handshaker_args;
bssl::UniquePtr<SSL_CTX> SetupCtx(SSL_CTX *old_ctx) const;
bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, SSL_SESSION *session,
std::unique_ptr<TestState> test_state) const;
};
bool ParseConfig(int argc, char **argv, bool is_shim, TestConfig *out_initial,
TestConfig *out_resume, TestConfig *out_retry);
bool SetTestConfig(SSL *ssl, const TestConfig *config);
const TestConfig *GetTestConfig(const SSL *ssl);
bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
bssl::UniquePtr<STACK_OF(X509)> *out_chain,
const std::string &file);
bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file);
#endif // HEADER_TEST_CONFIG

View File

@@ -0,0 +1,170 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#include "test_state.h"
#include <openssl/ssl.h>
#include "../../crypto/internal.h"
#include "../internal.h"
using namespace bssl;
static CRYPTO_once_t g_once = CRYPTO_ONCE_INIT;
static int g_state_index = 0;
// Some code treats the zero time special, so initialize the clock to a
// non-zero time.
static timeval g_clock = { 1234, 1234 };
static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
int index, long argl, void *argp) {
delete ((TestState *)ptr);
}
static void init_once() {
g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree);
if (g_state_index < 0) {
abort();
}
}
struct timeval *GetClock() {
CRYPTO_once(&g_once, init_once);
return &g_clock;
}
void AdvanceClock(unsigned seconds) {
CRYPTO_once(&g_once, init_once);
g_clock.tv_sec += seconds;
}
bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state) {
CRYPTO_once(&g_once, init_once);
// |SSL_set_ex_data| takes ownership of |state| only on success.
if (SSL_set_ex_data(ssl, g_state_index, state.get()) == 1) {
state.release();
return true;
}
return false;
}
TestState *GetTestState(const SSL *ssl) {
CRYPTO_once(&g_once, init_once);
return (TestState *)SSL_get_ex_data(ssl, g_state_index);
}
static void ssl_ctx_add_session(SSL_SESSION *session, void *void_param) {
SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(void_param);
UniquePtr<SSL_SESSION> new_session = SSL_SESSION_dup(
session, SSL_SESSION_INCLUDE_NONAUTH | SSL_SESSION_INCLUDE_TICKET);
if (new_session != nullptr) {
SSL_CTX_add_session(ctx, new_session.get());
}
}
void CopySessions(SSL_CTX *dst, const SSL_CTX *src) {
lh_SSL_SESSION_doall_arg(src->sessions, ssl_ctx_add_session, dst);
}
static void push_session(SSL_SESSION *session, void *arg) {
auto s = reinterpret_cast<std::vector<SSL_SESSION *> *>(arg);
s->push_back(session);
}
bool SerializeContextState(SSL_CTX *ctx, CBB *cbb) {
CBB out, ctx_sessions, ticket_keys;
uint8_t keys[48];
if (!CBB_add_u24_length_prefixed(cbb, &out) ||
!CBB_add_u16(&out, 0 /* version */) ||
!SSL_CTX_get_tlsext_ticket_keys(ctx, &keys, sizeof(keys)) ||
!CBB_add_u8_length_prefixed(&out, &ticket_keys) ||
!CBB_add_bytes(&ticket_keys, keys, sizeof(keys)) ||
!CBB_add_asn1(&out, &ctx_sessions, CBS_ASN1_SEQUENCE)) {
return false;
}
std::vector<SSL_SESSION *> sessions;
lh_SSL_SESSION_doall_arg(ctx->sessions, push_session, &sessions);
for (const auto &sess : sessions) {
if (!ssl_session_serialize(sess, &ctx_sessions)) {
return false;
}
}
return CBB_flush(cbb);
}
bool DeserializeContextState(CBS *cbs, SSL_CTX *ctx) {
CBS in, sessions, ticket_keys;
uint16_t version;
constexpr uint16_t kVersion = 0;
if (!CBS_get_u24_length_prefixed(cbs, &in) ||
!CBS_get_u16(&in, &version) ||
version > kVersion ||
!CBS_get_u8_length_prefixed(&in, &ticket_keys) ||
!SSL_CTX_set_tlsext_ticket_keys(ctx, CBS_data(&ticket_keys),
CBS_len(&ticket_keys)) ||
!CBS_get_asn1(&in, &sessions, CBS_ASN1_SEQUENCE)) {
return false;
}
while (CBS_len(&sessions)) {
UniquePtr<SSL_SESSION> session =
SSL_SESSION_parse(&sessions, ctx->x509_method, ctx->pool);
if (!session) {
return false;
}
SSL_CTX_add_session(ctx, session.get());
}
return true;
}
bool TestState::Serialize(CBB *cbb) const {
CBB out, pending, text;
if (!CBB_add_u24_length_prefixed(cbb, &out) ||
!CBB_add_u16(&out, 0 /* version */) ||
!CBB_add_u24_length_prefixed(&out, &pending) ||
(pending_session &&
!ssl_session_serialize(pending_session.get(), &pending)) ||
!CBB_add_u16_length_prefixed(&out, &text) ||
!CBB_add_bytes(
&text, reinterpret_cast<const uint8_t *>(msg_callback_text.data()),
msg_callback_text.length()) ||
!CBB_add_asn1_uint64(&out, g_clock.tv_sec) ||
!CBB_add_asn1_uint64(&out, g_clock.tv_usec) ||
!CBB_flush(cbb)) {
return false;
}
return true;
}
std::unique_ptr<TestState> TestState::Deserialize(CBS *cbs, SSL_CTX *ctx) {
CBS in, pending_session, text;
std::unique_ptr<TestState> out_state(new TestState());
uint16_t version;
constexpr uint16_t kVersion = 0;
uint64_t sec, usec;
if (!CBS_get_u24_length_prefixed(cbs, &in) ||
!CBS_get_u16(&in, &version) ||
version > kVersion ||
!CBS_get_u24_length_prefixed(&in, &pending_session) ||
!CBS_get_u16_length_prefixed(&in, &text)) {
return nullptr;
}
if (CBS_len(&pending_session)) {
out_state->pending_session = SSL_SESSION_parse(
&pending_session, ctx->x509_method, ctx->pool);
if (!out_state->pending_session) {
return nullptr;
}
}
out_state->msg_callback_text = std::string(
reinterpret_cast<const char *>(CBS_data(&text)), CBS_len(&text));
// TODO(2020-05-01): Make this unconditional & merge into above.
if (CBS_len(&in) > 0) {
if (!CBS_get_asn1_uint64(&in, &sec) ||
!CBS_get_asn1_uint64(&in, &usec)) {
return nullptr;
}
g_clock.tv_sec = sec;
g_clock.tv_usec = usec;
}
return out_state;
}

View File

@@ -0,0 +1,80 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef HEADER_TEST_STATE
#define HEADER_TEST_STATE
#include <openssl/base.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "mock_quic_transport.h"
struct TestState {
// Serialize writes |pending_session| and |msg_callback_text| to |out|, for
// use in split-handshake tests. We don't try to serialize every bit of test
// state, but serializing |pending_session| is necessary to exercise session
// resumption, and |msg_callback_text| is especially useful. In the general
// case, checks of state updated during the handshake can be skipped when
// |config->handoff|.
bool Serialize(CBB *out) const;
// Deserialize returns a new |TestState| from data written by |Serialize|.
static std::unique_ptr<TestState> Deserialize(CBS *cbs, SSL_CTX *ctx);
// async_bio is async BIO which pauses reads and writes.
BIO *async_bio = nullptr;
// packeted_bio is the packeted BIO which simulates read timeouts.
BIO *packeted_bio = nullptr;
std::unique_ptr<MockQuicTransport> quic_transport;
bool cert_ready = false;
bssl::UniquePtr<SSL_SESSION> session;
bssl::UniquePtr<SSL_SESSION> pending_session;
bool early_callback_called = false;
bool handshake_done = false;
// private_key is the underlying private key used when testing custom keys.
bssl::UniquePtr<EVP_PKEY> private_key;
// When private key methods are used, whether the private key was used.
bool used_private_key = false;
std::vector<uint8_t> private_key_result;
// private_key_retries is the number of times an asynchronous private key
// operation has been retried.
unsigned private_key_retries = 0;
bool got_new_session = false;
bssl::UniquePtr<SSL_SESSION> new_session;
bool ticket_decrypt_done = false;
bool alpn_select_done = false;
bool early_callback_ready = false;
bool custom_verify_ready = false;
std::string msg_callback_text;
bool msg_callback_ok = true;
// cert_verified is true if certificate verification has been driven to
// completion. This tests that the callback is not called again after this.
bool cert_verified = false;
int explicit_renegotiates = 0;
std::function<bool(const SSL_CLIENT_HELLO*)> get_handshake_hints_cb;
int last_message_received = -1;
};
bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state);
TestState *GetTestState(const SSL *ssl);
struct timeval *GetClock();
void AdvanceClock(unsigned seconds);
void CopySessions(SSL_CTX *dest, const SSL_CTX *src);
// SerializeContextState writes session material (sessions and ticket keys) from
// |ctx| into |cbb|.
bool SerializeContextState(SSL_CTX *ctx, CBB *cbb);
// DeserializeContextState updates |out| with material previously serialized by
// SerializeContextState.
bool DeserializeContextState(CBS *in, SSL_CTX *out);
#endif // HEADER_TEST_STATE