Add basic TLS Channel ID tests.

Change-Id: I7ccf2b8282dfa8f3985775e8b67edcf3c2949752
Reviewed-on: https://boringssl-review.googlesource.com/1606
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin 2014-08-24 01:46:07 -04:00 committed by Adam Langley
parent 4e99c52bf6
commit a08e49d17a
5 changed files with 137 additions and 4 deletions

View File

@ -48,6 +48,20 @@ static const TestConfig *GetConfigPtr(SSL *ssl) {
return (const TestConfig *)SSL_get_ex_data(ssl, g_ex_data_index);
}
static EVP_PKEY *LoadPrivateKey(const std::string &file) {
BIO *bio = BIO_new(BIO_s_file());
if (bio == NULL) {
return NULL;
}
if (!BIO_read_filename(bio, file.c_str())) {
BIO_free(bio);
return NULL;
}
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
return pkey;
}
static int early_callback_called = 0;
static int select_certificate_callback(const struct ssl_early_callback_ctx *ctx) {
@ -205,6 +219,8 @@ static SSL_CTX *setup_ctx(const TestConfig *config) {
SSL_CTX_set_cookie_generate_cb(ssl_ctx, cookie_generate_callback);
SSL_CTX_set_cookie_verify_cb(ssl_ctx, cookie_verify_callback);
ssl_ctx->tlsext_channel_id_enabled_new = 1;
DH_free(dh);
return ssl_ctx;
@ -300,6 +316,23 @@ static int do_exchange(SSL_SESSION **out_session,
if (config->cookie_exchange) {
SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
}
if (!config->expected_channel_id.empty()) {
SSL_enable_tls_channel_id(ssl);
}
if (!config->send_channel_id.empty()) {
EVP_PKEY *pkey = LoadPrivateKey(config->send_channel_id);
if (pkey == NULL) {
BIO_print_errors_fp(stdout);
return 1;
}
SSL_enable_tls_channel_id(ssl);
if (!SSL_set1_tls_channel_id(ssl, pkey)) {
EVP_PKEY_free(pkey);
BIO_print_errors_fp(stdout);
return 1;
}
EVP_PKEY_free(pkey);
}
BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
if (bio == NULL) {
@ -363,7 +396,7 @@ static int do_exchange(SSL_SESSION **out_session,
if (!config->expected_certificate_types.empty()) {
uint8_t *certificate_types;
int num_certificate_types =
SSL_get0_certificate_types(ssl, &certificate_types);
SSL_get0_certificate_types(ssl, &certificate_types);
if (num_certificate_types !=
(int)config->expected_certificate_types.size() ||
memcmp(certificate_types,
@ -386,6 +419,20 @@ static int do_exchange(SSL_SESSION **out_session,
}
}
if (!config->expected_channel_id.empty()) {
uint8_t channel_id[64];
if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) {
fprintf(stderr, "no channel id negotiated\n");
return 2;
}
if (config->expected_channel_id.size() != 64 ||
memcmp(config->expected_channel_id.data(),
channel_id, 64) != 0) {
fprintf(stderr, "channel id mismatch\n");
return 2;
}
}
if (config->write_different_record_sizes) {
if (config->is_dtls) {
fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIPwxu50c7LEhVNRYJFRWBUnoaz7JSos96T5hBp4rjyptoAoGCCqGSM49
AwEHoUQDQgAEzFSVTE5guxJRQ0VbZ8dicPs5e/DT7xpW7Yc9hq0VOchv7cbXuI/T
CwadDjGWX/oaz0ftFqrVmfkwZu+C58ioWg==
-----END EC PRIVATE KEY-----

View File

@ -2,8 +2,11 @@ package main
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"flag"
"fmt"
"io"
@ -26,11 +29,14 @@ const (
)
const (
rsaKeyFile = "key.pem"
ecdsaKeyFile = "ecdsa_key.pem"
rsaKeyFile = "key.pem"
ecdsaKeyFile = "ecdsa_key.pem"
channelIDKeyFile = "channel_id_key.pem"
)
var rsaCertificate, ecdsaCertificate Certificate
var channelIDKey *ecdsa.PrivateKey
var channelIDBytes []byte
func initCertificates() {
var err error
@ -43,6 +49,26 @@ func initCertificates() {
if err != nil {
panic(err)
}
channelIDPEMBlock, err := ioutil.ReadFile(channelIDKeyFile)
if err != nil {
panic(err)
}
channelIDDERBlock, _ := pem.Decode(channelIDPEMBlock)
if channelIDDERBlock.Type != "EC PRIVATE KEY" {
panic("bad key type")
}
channelIDKey, err = x509.ParseECPrivateKey(channelIDDERBlock.Bytes)
if err != nil {
panic(err)
}
if channelIDKey.Curve != elliptic.P256() {
panic("bad curve")
}
channelIDBytes = make([]byte, 64)
writeIntPadded(channelIDBytes[:32], channelIDKey.X)
writeIntPadded(channelIDBytes[32:], channelIDKey.Y)
}
var certificateOnce sync.Once
@ -84,6 +110,9 @@ type testCase struct {
// expectedVersion, if non-zero, specifies the TLS version that must be
// negotiated.
expectedVersion uint16
// expectChannelID controls whether the connection should have
// negotiated a Channel ID with channelIDKey.
expectChannelID bool
// messageLen is the length, in bytes, of the test message that will be
// sent.
messageLen int
@ -488,6 +517,18 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) e
return fmt.Errorf("got version %x, expected %x", vers, test.expectedVersion)
}
if test.expectChannelID {
channelID := tlsConn.ConnectionState().ChannelID
if channelID == nil {
return fmt.Errorf("no channel ID negotiated")
}
if channelID.Curve != channelIDKey.Curve ||
channelIDKey.X.Cmp(channelIDKey.X) != 0 ||
channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
return fmt.Errorf("incorrect channel ID")
}
}
if messageLen < 0 {
if test.protocol == dtls {
return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
@ -1141,7 +1182,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
),
})
// Client sends a V2ClientHello.
// Server parses a V2ClientHello.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
@ -1158,6 +1199,42 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
},
flags: flags,
})
// Client sends a Channel ID.
testCases = append(testCases, testCase{
protocol: protocol,
name: "ChannelID-Client" + suffix,
config: Config{
RequestChannelID: true,
Bugs: ProtocolBugs{
MaxHandshakeRecordLength: maxHandshakeRecordLength,
},
},
flags: append(flags,
"-send-channel-id", channelIDKeyFile,
),
resumeSession: true,
expectChannelID: true,
})
// Server accepts a Channel ID.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "ChannelID-Server" + suffix,
config: Config{
ChannelID: channelIDKey,
Bugs: ProtocolBugs{
MaxHandshakeRecordLength: maxHandshakeRecordLength,
},
},
flags: append(flags,
"-expect-channel-id",
base64.StdEncoding.EncodeToString(channelIDBytes),
),
resumeSession: true,
expectChannelID: true,
})
} else {
testCases = append(testCases, testCase{
protocol: protocol,

View File

@ -64,12 +64,14 @@ const StringFlag kStringFlags[] = {
{ "-advertise-npn", &TestConfig::advertise_npn },
{ "-expect-next-proto", &TestConfig::expected_next_proto },
{ "-select-next-proto", &TestConfig::select_next_proto },
{ "-send-channel-id", &TestConfig::send_channel_id },
};
const size_t kNumStringFlags = sizeof(kStringFlags) / sizeof(kStringFlags[0]);
const StringFlag kBase64Flags[] = {
{ "-expect-certificate-types", &TestConfig::expected_certificate_types },
{ "-expect-channel-id", &TestConfig::expected_channel_id },
};
const size_t kNumBase64Flags = sizeof(kBase64Flags) / sizeof(kBase64Flags[0]);

View File

@ -43,6 +43,8 @@ struct TestConfig {
bool no_tls1;
bool no_ssl3;
bool cookie_exchange;
std::string expected_channel_id;
std::string send_channel_id;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);