Add tests for ALPN support.
Both as client and as server. Also tests that ALPN causes False Start to kick in. Change-Id: Ib570346f3c511834152cd2df2ef29541946d3ab4 Reviewed-on: https://boringssl-review.googlesource.com/1753 Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
parent
fa055a2b77
commit
ae2888fbdc
@ -139,6 +139,29 @@ static int next_proto_select_callback(SSL* ssl,
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static int alpn_select_callback(SSL* ssl,
|
||||
const uint8_t** out,
|
||||
uint8_t* outlen,
|
||||
const uint8_t* in,
|
||||
unsigned inlen,
|
||||
void* arg) {
|
||||
const TestConfig *config = GetConfigPtr(ssl);
|
||||
if (config->select_alpn.empty())
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
|
||||
if (!config->expected_advertised_alpn.empty() &&
|
||||
(config->expected_advertised_alpn.size() != inlen ||
|
||||
memcmp(config->expected_advertised_alpn.data(),
|
||||
in, inlen) != 0)) {
|
||||
fprintf(stderr, "bad ALPN select callback inputs\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
*out = (const uint8_t*)config->select_alpn.data();
|
||||
*outlen = config->select_alpn.size();
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static int cookie_generate_callback(SSL *ssl, uint8_t *cookie, size_t *cookie_len) {
|
||||
*cookie_len = 32;
|
||||
memset(cookie, 42, *cookie_len);
|
||||
@ -213,8 +236,13 @@ static SSL_CTX *setup_ctx(const TestConfig *config) {
|
||||
|
||||
SSL_CTX_set_next_protos_advertised_cb(
|
||||
ssl_ctx, next_protos_advertised_callback, NULL);
|
||||
SSL_CTX_set_next_proto_select_cb(
|
||||
ssl_ctx, next_proto_select_callback, NULL);
|
||||
if (!config->select_next_proto.empty()) {
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_callback, NULL);
|
||||
}
|
||||
|
||||
if (!config->select_alpn.empty()) {
|
||||
SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_callback, NULL);
|
||||
}
|
||||
|
||||
SSL_CTX_set_cookie_generate_cb(ssl_ctx, cookie_generate_callback);
|
||||
SSL_CTX_set_cookie_verify_cb(ssl_ctx, cookie_verify_callback);
|
||||
@ -339,6 +367,10 @@ static int do_exchange(SSL_SESSION **out_session,
|
||||
if (!config->host_name.empty()) {
|
||||
SSL_set_tlsext_host_name(ssl, config->host_name.c_str());
|
||||
}
|
||||
if (!config->advertise_alpn.empty()) {
|
||||
SSL_set_alpn_protos(ssl, (const uint8_t *)config->advertise_alpn.data(),
|
||||
config->advertise_alpn.size());
|
||||
}
|
||||
|
||||
BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
|
||||
if (bio == NULL) {
|
||||
@ -425,6 +457,18 @@ static int do_exchange(SSL_SESSION **out_session,
|
||||
}
|
||||
}
|
||||
|
||||
if (!config->expected_alpn.empty()) {
|
||||
const uint8_t *alpn_proto;
|
||||
unsigned alpn_proto_len;
|
||||
SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len);
|
||||
if (alpn_proto_len != config->expected_alpn.size() ||
|
||||
memcmp(alpn_proto, config->expected_alpn.data(),
|
||||
alpn_proto_len) != 0) {
|
||||
fprintf(stderr, "negotiated alpn proto mismatch\n");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config->expected_channel_id.empty()) {
|
||||
uint8_t channel_id[64];
|
||||
if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) {
|
||||
|
@ -113,6 +113,9 @@ type testCase struct {
|
||||
// expectChannelID controls whether the connection should have
|
||||
// negotiated a Channel ID with channelIDKey.
|
||||
expectChannelID bool
|
||||
// expectedNextProto controls whether the connection should
|
||||
// negotiate a next protocol via NPN or ALPN.
|
||||
expectedNextProto string
|
||||
// messageLen is the length, in bytes, of the test message that will be
|
||||
// sent.
|
||||
messageLen int
|
||||
@ -514,6 +517,12 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) e
|
||||
}
|
||||
}
|
||||
|
||||
if expected := test.expectedNextProto; expected != "" {
|
||||
if actual := tlsConn.ConnectionState().NegotiatedProtocol; actual != expected {
|
||||
return fmt.Errorf("next proto mismatch: got %s, wanted %s", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
if test.shimWritesFirst {
|
||||
var buf [5]byte
|
||||
_, err := io.ReadFull(tlsConn, buf[:])
|
||||
@ -1129,13 +1138,13 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
|
||||
protocol: protocol,
|
||||
name: "NPN-Client" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
},
|
||||
},
|
||||
flags: append(flags, "-select-next-proto", "foo"),
|
||||
expectedNextProto: "foo",
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
@ -1150,6 +1159,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
|
||||
flags: append(flags,
|
||||
"-advertise-npn", "\x03foo\x03bar\x03baz",
|
||||
"-expect-next-proto", "bar"),
|
||||
expectedNextProto: "bar",
|
||||
})
|
||||
|
||||
// Client does False Start and negotiates NPN.
|
||||
@ -1171,6 +1181,25 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
|
||||
resumeSession: true,
|
||||
})
|
||||
|
||||
// Client does False Start and negotiates ALPN.
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
name: "FalseStart-ALPN" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
Bugs: ProtocolBugs{
|
||||
ExpectFalseStart: true,
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
},
|
||||
},
|
||||
flags: append(flags,
|
||||
"-false-start",
|
||||
"-advertise-alpn", "\x03foo"),
|
||||
shimWritesFirst: true,
|
||||
resumeSession: true,
|
||||
})
|
||||
|
||||
// False Start without session tickets.
|
||||
testCases = append(testCases, testCase{
|
||||
name: "FalseStart-SessionTicketsDisabled",
|
||||
@ -1407,6 +1436,32 @@ func addExtensionTests() {
|
||||
flags: []string{"-expect-server-name", "example.com"},
|
||||
resumeSession: true,
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
testType: clientTest,
|
||||
name: "ALPNClient",
|
||||
config: Config{
|
||||
NextProtos: []string{"foo"},
|
||||
},
|
||||
flags: []string{
|
||||
"-advertise-alpn", "\x03foo\x03bar\x03baz",
|
||||
"-expect-alpn", "foo",
|
||||
},
|
||||
expectedNextProto: "foo",
|
||||
resumeSession: true,
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "ALPNServer",
|
||||
config: Config{
|
||||
NextProtos: []string{"foo", "bar", "baz"},
|
||||
},
|
||||
flags: []string{
|
||||
"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
|
||||
"-select-alpn", "foo",
|
||||
},
|
||||
expectedNextProto: "foo",
|
||||
resumeSession: true,
|
||||
})
|
||||
}
|
||||
|
||||
func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
|
||||
|
@ -68,6 +68,10 @@ const StringFlag kStringFlags[] = {
|
||||
{ "-select-next-proto", &TestConfig::select_next_proto },
|
||||
{ "-send-channel-id", &TestConfig::send_channel_id },
|
||||
{ "-host-name", &TestConfig::host_name },
|
||||
{ "-advertise-alpn", &TestConfig::advertise_alpn },
|
||||
{ "-expect-alpn", &TestConfig::expected_alpn },
|
||||
{ "-expect-advertised-alpn", &TestConfig::expected_advertised_alpn },
|
||||
{ "-select-alpn", &TestConfig::select_alpn },
|
||||
};
|
||||
|
||||
const size_t kNumStringFlags = sizeof(kStringFlags) / sizeof(kStringFlags[0]);
|
||||
|
@ -48,6 +48,10 @@ struct TestConfig {
|
||||
bool shim_writes_first;
|
||||
bool tls_d5_bug;
|
||||
std::string host_name;
|
||||
std::string advertise_alpn;
|
||||
std::string expected_alpn;
|
||||
std::string expected_advertised_alpn;
|
||||
std::string select_alpn;
|
||||
};
|
||||
|
||||
bool ParseConfig(int argc, char **argv, TestConfig *out_config);
|
||||
|
Loading…
Reference in New Issue
Block a user