Add support for the new QUIC TLS extension codepoint

IETF QUIC draft 33 is replacing the TLS extension
codepoint for QUIC transport parameters from 0xffa5
to 57. To support multiple versions of Chrome, we
need to support both codepoints in BoringSSL. This
CL adds support for the new codepoint in a way that
can be enabled on individual connections.

Note that when BoringSSL is not in QUIC mode, it
will error if it sees the new codepoint as a server
but it will ignore the legacy codepoint as that could
be a different private usage of that codepoint.

Change-Id: I314f8f0b169cedd96eeccc42b44153e97044388c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44704
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
chromium-5359
David Schinazi 4 years ago committed by CQ bot account: commit-bot@chromium.org
parent f8f35c9555
commit 3d8b8c3df2
  1. 2
      include/openssl/base.h
  2. 6
      include/openssl/ssl.h
  3. 11
      include/openssl/tls1.h
  4. 4
      ssl/internal.h
  5. 10
      ssl/ssl_lib.cc
  6. 76
      ssl/ssl_test.cc
  7. 132
      ssl/t1_lib.cc
  8. 45
      ssl/test/runner/common.go
  9. 6
      ssl/test/runner/conn.go
  10. 68
      ssl/test/runner/handshake_client.go
  11. 171
      ssl/test/runner/handshake_messages.go
  12. 9
      ssl/test/runner/handshake_server.go
  13. 299
      ssl/test/runner/runner.go
  14. 4
      ssl/test/test_config.cc
  15. 2
      ssl/test/test_config.h

@ -191,7 +191,7 @@ extern "C" {
// A consumer may use this symbol in the preprocessor to temporarily build
// against multiple revisions of BoringSSL at the same time. It is not
// recommended to do so for longer than is necessary.
#define BORINGSSL_API_VERSION 12
#define BORINGSSL_API_VERSION 13
#if defined(BORINGSSL_SHARED_LIBRARY)

@ -3384,6 +3384,12 @@ OPENSSL_EXPORT int SSL_set_quic_transport_params(SSL *ssl,
OPENSSL_EXPORT void SSL_get_peer_quic_transport_params(
const SSL *ssl, const uint8_t **out_params, size_t *out_params_len);
// SSL_set_quic_use_legacy_codepoint configures whether to use the legacy QUIC
// extension codepoint 0xffa5 as opposed to the official value 57. Call with
// |use_legacy| set to 1 to use 0xffa5 and call with 0 to use 57. The default
// value for this is currently 1 but it will change to 0 at a later date.
OPENSSL_EXPORT void SSL_set_quic_use_legacy_codepoint(SSL *ssl, int use_legacy);
// SSL_set_quic_early_data_context configures a context string in QUIC servers
// for accepting early data. If a resumption connection offers early data, the
// server will check if the value matches that of the connection which minted

@ -206,10 +206,13 @@ extern "C" {
// ExtensionType value from draft-ietf-tokbind-negotiation-10
#define TLSEXT_TYPE_token_binding 24
// ExtensionType value from draft-ietf-quic-tls. Note that this collides with
// TLS-LTS and, based on scans, something else too. Since it's QUIC-only, that
// shouldn't be a problem in practice.
#define TLSEXT_TYPE_quic_transport_parameters 0xffa5
// ExtensionType value from draft-ietf-quic-tls. Drafts 00 through 32 use
// 0xffa5 which is part of the Private Use section of the registry, and it
// collides with TLS-LTS and, based on scans, something else too (though this
// hasn't been a problem in practice since it's QUIC-only). Drafts 33 onward
// use the value 57 which was officially registered with IANA.
#define TLSEXT_TYPE_quic_transport_parameters_legacy 0xffa5
#define TLSEXT_TYPE_quic_transport_parameters 57
// ExtensionType value from RFC8879
#define TLSEXT_TYPE_cert_compression 27

@ -2790,6 +2790,10 @@ struct SSL_CONFIG {
// jdk11_workaround is whether to disable TLS 1.3 for JDK 11 clients, as a
// workaround for https://bugs.openjdk.java.net/browse/JDK-8211806.
bool jdk11_workaround : 1;
// QUIC drafts up to and including 32 used a different TLS extension
// codepoint to convey QUIC's transport parameters.
bool quic_use_legacy_codepoint : 1;
};
// From RFC 8446, used in determining PSK modes.

@ -730,7 +730,8 @@ SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg)
retain_only_sha256_of_client_certs(false),
handoff(false),
shed_handshake_config(false),
jdk11_workaround(false) {
jdk11_workaround(false),
quic_use_legacy_codepoint(true) {
assert(ssl);
}
@ -2958,6 +2959,13 @@ void SSL_set_jdk11_workaround(SSL *ssl, int enable) {
ssl->config->jdk11_workaround = !!enable;
}
void SSL_set_quic_use_legacy_codepoint(SSL *ssl, int use_legacy) {
if (!ssl->config) {
return;
}
ssl->config->quic_use_legacy_codepoint = !!use_legacy;
}
int SSL_clear(SSL *ssl) {
if (!ssl->config) {
return 0; // SSL_clear may not be used after shedding config.

@ -6073,6 +6073,82 @@ TEST_F(QUICMethodTest, ServerRejectsMissingTransportParams) {
ASSERT_TRUE(RunQUICHandshakesAndExpectError(ExpectedError::kClientError));
}
TEST_F(QUICMethodTest, QuicLegacyCodepointEnabled) {
const SSL_QUIC_METHOD quic_method = DefaultQUICMethod();
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
uint8_t kClientParams[] = {1, 2, 3, 4};
uint8_t kServerParams[] = {5, 6, 7};
SSL_set_quic_use_legacy_codepoint(client_.get(), 1);
SSL_set_quic_use_legacy_codepoint(server_.get(), 1);
ASSERT_TRUE(SSL_set_quic_transport_params(client_.get(), kClientParams,
sizeof(kClientParams)));
ASSERT_TRUE(SSL_set_quic_transport_params(server_.get(), kServerParams,
sizeof(kServerParams)));
ASSERT_TRUE(CompleteHandshakesForQUIC());
ExpectReceivedTransportParamsEqual(client_.get(), kServerParams);
ExpectReceivedTransportParamsEqual(server_.get(), kClientParams);
}
TEST_F(QUICMethodTest, QuicLegacyCodepointDisabled) {
const SSL_QUIC_METHOD quic_method = DefaultQUICMethod();
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
uint8_t kClientParams[] = {1, 2, 3, 4};
uint8_t kServerParams[] = {5, 6, 7};
SSL_set_quic_use_legacy_codepoint(client_.get(), 0);
SSL_set_quic_use_legacy_codepoint(server_.get(), 0);
ASSERT_TRUE(SSL_set_quic_transport_params(client_.get(), kClientParams,
sizeof(kClientParams)));
ASSERT_TRUE(SSL_set_quic_transport_params(server_.get(), kServerParams,
sizeof(kServerParams)));
ASSERT_TRUE(CompleteHandshakesForQUIC());
ExpectReceivedTransportParamsEqual(client_.get(), kServerParams);
ExpectReceivedTransportParamsEqual(server_.get(), kClientParams);
}
TEST_F(QUICMethodTest, QuicLegacyCodepointClientOnly) {
const SSL_QUIC_METHOD quic_method = DefaultQUICMethod();
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
uint8_t kClientParams[] = {1, 2, 3, 4};
uint8_t kServerParams[] = {5, 6, 7};
SSL_set_quic_use_legacy_codepoint(client_.get(), 1);
SSL_set_quic_use_legacy_codepoint(server_.get(), 0);
ASSERT_TRUE(SSL_set_quic_transport_params(client_.get(), kClientParams,
sizeof(kClientParams)));
ASSERT_TRUE(SSL_set_quic_transport_params(server_.get(), kServerParams,
sizeof(kServerParams)));
ASSERT_TRUE(RunQUICHandshakesAndExpectError(ExpectedError::kServerError));
}
TEST_F(QUICMethodTest, QuicLegacyCodepointServerOnly) {
const SSL_QUIC_METHOD quic_method = DefaultQUICMethod();
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
uint8_t kClientParams[] = {1, 2, 3, 4};
uint8_t kServerParams[] = {5, 6, 7};
SSL_set_quic_use_legacy_codepoint(client_.get(), 0);
SSL_set_quic_use_legacy_codepoint(server_.get(), 1);
ASSERT_TRUE(SSL_set_quic_transport_params(client_.get(), kClientParams,
sizeof(kClientParams)));
ASSERT_TRUE(SSL_set_quic_transport_params(server_.get(), kServerParams,
sizeof(kServerParams)));
ASSERT_TRUE(RunQUICHandshakesAndExpectError(ExpectedError::kServerError));
}
extern "C" {
int BORINGSSL_enum_c_type_test(void);
}

@ -2773,8 +2773,8 @@ static bool ext_token_binding_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
// QUIC Transport Parameters
static bool ext_quic_transport_params_add_clienthello(SSL_HANDSHAKE *hs,
CBB *out) {
static bool ext_quic_transport_params_add_clienthello_impl(
SSL_HANDSHAKE *hs, CBB *out, bool use_legacy_codepoint) {
if (hs->config->quic_transport_params.empty() && !hs->ssl->quic_method) {
return true;
}
@ -2786,9 +2786,18 @@ static bool ext_quic_transport_params_add_clienthello(SSL_HANDSHAKE *hs,
return false;
}
assert(hs->min_version > TLS1_2_VERSION);
if (use_legacy_codepoint != hs->config->quic_use_legacy_codepoint) {
// Do nothing, we'll send the other codepoint.
return true;
}
uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters;
if (hs->config->quic_use_legacy_codepoint) {
extension_type = TLSEXT_TYPE_quic_transport_parameters_legacy;
}
CBB contents;
if (!CBB_add_u16(out, TLSEXT_TYPE_quic_transport_parameters) ||
if (!CBB_add_u16(out, extension_type) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_bytes(&contents, hs->config->quic_transport_params.data(),
hs->config->quic_transport_params.size()) ||
@ -2798,31 +2807,57 @@ static bool ext_quic_transport_params_add_clienthello(SSL_HANDSHAKE *hs,
return true;
}
static bool ext_quic_transport_params_parse_serverhello(SSL_HANDSHAKE *hs,
uint8_t *out_alert,
CBS *contents) {
static bool ext_quic_transport_params_add_clienthello(SSL_HANDSHAKE *hs,
CBB *out) {
return ext_quic_transport_params_add_clienthello_impl(
hs, out, /*use_legacy_codepoint=*/false);
}
static bool ext_quic_transport_params_add_clienthello_legacy(SSL_HANDSHAKE *hs,
CBB *out) {
return ext_quic_transport_params_add_clienthello_impl(
hs, out, /*use_legacy_codepoint=*/true);
}
static bool ext_quic_transport_params_parse_serverhello_impl(
SSL_HANDSHAKE *hs, uint8_t *out_alert, CBS *contents,
bool used_legacy_codepoint) {
SSL *const ssl = hs->ssl;
if (contents == nullptr) {
if (used_legacy_codepoint != hs->config->quic_use_legacy_codepoint) {
// Silently ignore because we expect the other QUIC codepoint.
return true;
}
if (!ssl->quic_method) {
return true;
}
assert(ssl->quic_method);
*out_alert = SSL_AD_MISSING_EXTENSION;
return false;
}
if (!ssl->quic_method) {
*out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
return false;
}
// QUIC requires TLS 1.3.
// The extensions parser will check for unsolicited extensions before
// calling the callback.
assert(ssl->quic_method != nullptr);
assert(ssl_protocol_version(ssl) == TLS1_3_VERSION);
assert(used_legacy_codepoint == hs->config->quic_use_legacy_codepoint);
return ssl->s3->peer_quic_transport_params.CopyFrom(*contents);
}
static bool ext_quic_transport_params_parse_clienthello(SSL_HANDSHAKE *hs,
static bool ext_quic_transport_params_parse_serverhello(SSL_HANDSHAKE *hs,
uint8_t *out_alert,
CBS *contents) {
return ext_quic_transport_params_parse_serverhello_impl(
hs, out_alert, contents, /*used_legacy_codepoint=*/false);
}
static bool ext_quic_transport_params_parse_serverhello_legacy(
SSL_HANDSHAKE *hs, uint8_t *out_alert, CBS *contents) {
return ext_quic_transport_params_parse_serverhello_impl(
hs, out_alert, contents, /*used_legacy_codepoint=*/true);
}
static bool ext_quic_transport_params_parse_clienthello_impl(
SSL_HANDSHAKE *hs, uint8_t *out_alert, CBS *contents,
bool used_legacy_codepoint) {
SSL *const ssl = hs->ssl;
if (!contents) {
if (!ssl->quic_method) {
@ -2833,29 +2868,72 @@ static bool ext_quic_transport_params_parse_clienthello(SSL_HANDSHAKE *hs,
// for QUIC.
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED);
*out_alert = SSL_AD_INTERNAL_ERROR;
return false;
}
if (used_legacy_codepoint != hs->config->quic_use_legacy_codepoint) {
// Silently ignore because we expect the other QUIC codepoint.
return true;
}
*out_alert = SSL_AD_MISSING_EXTENSION;
return false;
}
if (!ssl->quic_method) {
if (used_legacy_codepoint) {
// Ignore the legacy private-use codepoint because that could be sent
// to mean something else than QUIC transport parameters.
return true;
}
// Fail if we received the codepoint registered with IANA for QUIC
// because that is not allowed outside of QUIC.
*out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
return false;
}
assert(ssl_protocol_version(ssl) == TLS1_3_VERSION);
if (used_legacy_codepoint != hs->config->quic_use_legacy_codepoint) {
// Silently ignore because we expect the other QUIC codepoint.
return true;
}
return ssl->s3->peer_quic_transport_params.CopyFrom(*contents);
}
static bool ext_quic_transport_params_add_serverhello(SSL_HANDSHAKE *hs,
CBB *out) {
static bool ext_quic_transport_params_parse_clienthello(SSL_HANDSHAKE *hs,
uint8_t *out_alert,
CBS *contents) {
return ext_quic_transport_params_parse_clienthello_impl(
hs, out_alert, contents, /*used_legacy_codepoint=*/false);
}
static bool ext_quic_transport_params_parse_clienthello_legacy(
SSL_HANDSHAKE *hs, uint8_t *out_alert, CBS *contents) {
return ext_quic_transport_params_parse_clienthello_impl(
hs, out_alert, contents, /*used_legacy_codepoint=*/true);
}
static bool ext_quic_transport_params_add_serverhello_impl(
SSL_HANDSHAKE *hs, CBB *out, bool use_legacy_codepoint) {
if (hs->ssl->quic_method == nullptr && use_legacy_codepoint) {
// Ignore the legacy private-use codepoint because that could be sent
// to mean something else than QUIC transport parameters.
return true;
}
assert(hs->ssl->quic_method != nullptr);
if (hs->config->quic_transport_params.empty()) {
// Transport parameters must be set when using QUIC.
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED);
return false;
}
if (use_legacy_codepoint != hs->config->quic_use_legacy_codepoint) {
// Do nothing, we'll send the other codepoint.
return true;
}
uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters;
if (hs->config->quic_use_legacy_codepoint) {
extension_type = TLSEXT_TYPE_quic_transport_parameters_legacy;
}
CBB contents;
if (!CBB_add_u16(out, TLSEXT_TYPE_quic_transport_parameters) ||
if (!CBB_add_u16(out, extension_type) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_bytes(&contents, hs->config->quic_transport_params.data(),
hs->config->quic_transport_params.size()) ||
@ -2866,6 +2944,18 @@ static bool ext_quic_transport_params_add_serverhello(SSL_HANDSHAKE *hs,
return true;
}
static bool ext_quic_transport_params_add_serverhello(SSL_HANDSHAKE *hs,
CBB *out) {
return ext_quic_transport_params_add_serverhello_impl(
hs, out, /*use_legacy_codepoint=*/false);
}
static bool ext_quic_transport_params_add_serverhello_legacy(SSL_HANDSHAKE *hs,
CBB *out) {
return ext_quic_transport_params_add_serverhello_impl(
hs, out, /*use_legacy_codepoint=*/true);
}
// Delegated credentials.
//
// https://tools.ietf.org/html/draft-ietf-tls-subcerts
@ -3313,6 +3403,14 @@ static const struct tls_extension kExtensions[] = {
ext_quic_transport_params_parse_clienthello,
ext_quic_transport_params_add_serverhello,
},
{
TLSEXT_TYPE_quic_transport_parameters_legacy,
NULL,
ext_quic_transport_params_add_clienthello_legacy,
ext_quic_transport_params_parse_serverhello_legacy,
ext_quic_transport_params_parse_clienthello_legacy,
ext_quic_transport_params_add_serverhello_legacy,
},
{
TLSEXT_TYPE_token_binding,
NULL,

@ -119,11 +119,12 @@ const (
extensionCertificateAuthorities uint16 = 47
extensionSignatureAlgorithmsCert uint16 = 50
extensionKeyShare uint16 = 51
extensionQUICTransportParams uint16 = 57 // draft-ietf-quic-tls-33 and later
extensionCustom uint16 = 1234 // not IANA assigned
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionApplicationSettings uint16 = 17513 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01
extensionQUICTransportParams uint16 = 0xffa5 // draft-ietf-quic-tls-13
extensionQUICTransportParamsLegacy uint16 = 0xffa5 // draft-ietf-quic-tls-32 and earlier
extensionChannelID uint16 = 30032 // not IANA assigned
extensionDelegatedCredentials uint16 = 0x22 // draft-ietf-tls-subcerts-06
extensionDuplicate uint16 = 0xffff // not IANA assigned
@ -263,6 +264,7 @@ type ConnectionState struct {
PeerSignatureAlgorithm signatureAlgorithm // algorithm used by the peer in the handshake
CurveID CurveID // the curve used in ECDHE
QUICTransportParams []byte // the QUIC transport params received from the peer
QUICTransportParamsLegacy []byte // the legacy QUIC transport params received from the peer
HasApplicationSettings bool // whether ALPS was negotiated
PeerApplicationSettings []byte // application settings received from the peer
}
@ -339,6 +341,43 @@ type CertCompressionAlg struct {
Decompress func(out, in []byte) bool
}
// QUICUseCodepoint controls which TLS extension codepoint is used to convey the
// QUIC transport parameters. QUICUseCodepointStandard means use 57,
// QUICUseCodepointLegacy means use legacy value 0xff5a, QUICUseCodepointBoth
// means use both. QUICUseCodepointNeither means do not send transport
// parameters.
type QUICUseCodepoint int
const (
QUICUseCodepointStandard QUICUseCodepoint = iota
QUICUseCodepointLegacy
QUICUseCodepointBoth
QUICUseCodepointNeither
NumQUICUseCodepoints
)
func (c QUICUseCodepoint) IncludeStandard() bool {
return c == QUICUseCodepointStandard || c == QUICUseCodepointBoth
}
func (c QUICUseCodepoint) IncludeLegacy() bool {
return c == QUICUseCodepointLegacy || c == QUICUseCodepointBoth
}
func (c QUICUseCodepoint) String() string {
switch c {
case QUICUseCodepointStandard:
return "Standard"
case QUICUseCodepointLegacy:
return "Legacy"
case QUICUseCodepointBoth:
return "Both"
case QUICUseCodepointNeither:
return "Neither"
}
panic("unknown value")
}
// A Config structure is used to configure a TLS client or server.
// After one has been passed to a TLS function it must not be
// modified. A Config may be reused; the tls package will also not
@ -510,6 +549,10 @@ type Config struct {
// transport parameters extension.
QUICTransportParams []byte
// QUICTransportParamsUseLegacyCodepoint controls which TLS extension
// codepoint is used to convey the QUIC transport parameters.
QUICTransportParamsUseLegacyCodepoint QUICUseCodepoint
CertCompressionAlgs map[uint16]CertCompressionAlg
// Bugs specifies optional misbehaviour to be used for testing other

@ -61,8 +61,11 @@ type Conn struct {
// not applicable.
curveID CurveID
// quicTransportParams contains the QUIC transport params received
// by the peer.
// by the peer using codepoint 57.
quicTransportParams []byte
// quicTransportParams contains the QUIC transport params received
// by the peer using legacy codepoint 0xffa5.
quicTransportParamsLegacy []byte
clientRandom, serverRandom [32]byte
earlyExporterSecret []byte
@ -1895,6 +1898,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.PeerSignatureAlgorithm = c.peerSignatureAlgorithm
state.CurveID = c.curveID
state.QUICTransportParams = c.quicTransportParams
state.QUICTransportParamsLegacy = c.quicTransportParamsLegacy
state.HasApplicationSettings = c.hasApplicationSettings
state.PeerApplicationSettings = c.peerApplicationSettings
}

@ -115,34 +115,44 @@ func (c *Conn) clientHandshake() error {
prefixExtensions = append(prefixExtensions, extensionNextProtoNeg)
}
quicTransportParams := c.config.QUICTransportParams
quicTransportParamsLegacy := c.config.QUICTransportParams
if !c.config.QUICTransportParamsUseLegacyCodepoint.IncludeStandard() {
quicTransportParams = nil
}
if !c.config.QUICTransportParamsUseLegacyCodepoint.IncludeLegacy() {
quicTransportParamsLegacy = nil
}
minVersion := c.config.minVersion(c.isDTLS)
maxVersion := c.config.maxVersion(c.isDTLS)
hello := &clientHelloMsg{
isDTLS: c.isDTLS,
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: !c.config.Bugs.NoOCSPStapling,
sctListSupported: !c.config.Bugs.NoSignedCertificateTimestamps,
serverName: c.config.ServerName,
echIsInner: c.config.Bugs.SendECHIsInner,
supportedCurves: c.config.curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(c.config.NextProtos) > 0,
secureRenegotiation: []byte{},
alpnProtocols: c.config.NextProtos,
quicTransportParams: c.config.QUICTransportParams,
duplicateExtension: c.config.Bugs.DuplicateExtension,
channelIDSupported: c.config.ChannelID != nil,
tokenBindingParams: c.config.TokenBindingParams,
tokenBindingVersion: c.config.TokenBindingVersion,
extendedMasterSecret: maxVersion >= VersionTLS10,
srtpProtectionProfiles: c.config.SRTPProtectionProfiles,
srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
customExtension: c.config.Bugs.CustomExtension,
omitExtensions: c.config.Bugs.OmitExtensions,
emptyExtensions: c.config.Bugs.EmptyExtensions,
delegatedCredentials: !c.config.Bugs.DisableDelegatedCredentials,
prefixExtensions: prefixExtensions,
isDTLS: c.isDTLS,
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: !c.config.Bugs.NoOCSPStapling,
sctListSupported: !c.config.Bugs.NoSignedCertificateTimestamps,
serverName: c.config.ServerName,
echIsInner: c.config.Bugs.SendECHIsInner,
supportedCurves: c.config.curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(c.config.NextProtos) > 0,
secureRenegotiation: []byte{},
alpnProtocols: c.config.NextProtos,
quicTransportParams: quicTransportParams,
quicTransportParamsLegacy: quicTransportParamsLegacy,
duplicateExtension: c.config.Bugs.DuplicateExtension,
channelIDSupported: c.config.ChannelID != nil,
tokenBindingParams: c.config.TokenBindingParams,
tokenBindingVersion: c.config.TokenBindingVersion,
extendedMasterSecret: maxVersion >= VersionTLS10,
srtpProtectionProfiles: c.config.SRTPProtectionProfiles,
srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
customExtension: c.config.Bugs.CustomExtension,
omitExtensions: c.config.Bugs.OmitExtensions,
emptyExtensions: c.config.Bugs.EmptyExtensions,
delegatedCredentials: !c.config.Bugs.DisableDelegatedCredentials,
prefixExtensions: prefixExtensions,
}
if maxVersion >= VersionTLS13 {
@ -1759,6 +1769,14 @@ func (hs *clientHandshakeState) processServerExtensions(serverExtensions *server
c.quicTransportParams = serverExtensions.quicTransportParams
}
if len(serverExtensions.quicTransportParamsLegacy) > 0 {
if c.vers < VersionTLS13 {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: server sent QUIC transport params for TLS version less than 1.3")
}
c.quicTransportParamsLegacy = serverExtensions.quicTransportParamsLegacy
}
if serverExtensions.hasApplicationSettings {
if c.vers < VersionTLS13 {
return errors.New("tls: server sent application settings at invalid version")

@ -292,55 +292,56 @@ type clientECH struct {
}
type clientHelloMsg struct {
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
cookie []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
serverName string
clientECH *clientECH
echIsInner []byte
ocspStapling bool
supportedCurves []CurveID
supportedPoints []uint8
hasKeyShares bool
keyShares []keyShareEntry
keySharesRaw []byte
trailingKeyShareData bool
pskIdentities []pskIdentity
pskKEModes []byte
pskBinders [][]uint8
hasEarlyData bool
tls13Cookie []byte
ticketSupported bool
sessionTicket []uint8
signatureAlgorithms []signatureAlgorithm
signatureAlgorithmsCert []signatureAlgorithm
supportedVersions []uint16
secureRenegotiation []byte
alpnProtocols []string
quicTransportParams []byte
duplicateExtension bool
channelIDSupported bool
tokenBindingParams []byte
tokenBindingVersion uint16
extendedMasterSecret bool
srtpProtectionProfiles []uint16
srtpMasterKeyIdentifier string
sctListSupported bool
customExtension string
hasGREASEExtension bool
omitExtensions bool
emptyExtensions bool
pad int
compressedCertAlgs []uint16
delegatedCredentials bool
alpsProtocols []string
prefixExtensions []uint16
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
cookie []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
serverName string
clientECH *clientECH
echIsInner []byte
ocspStapling bool
supportedCurves []CurveID
supportedPoints []uint8
hasKeyShares bool
keyShares []keyShareEntry
keySharesRaw []byte
trailingKeyShareData bool
pskIdentities []pskIdentity
pskKEModes []byte
pskBinders [][]uint8
hasEarlyData bool
tls13Cookie []byte
ticketSupported bool
sessionTicket []uint8
signatureAlgorithms []signatureAlgorithm
signatureAlgorithmsCert []signatureAlgorithm
supportedVersions []uint16
secureRenegotiation []byte
alpnProtocols []string
quicTransportParams []byte
quicTransportParamsLegacy []byte
duplicateExtension bool
channelIDSupported bool
tokenBindingParams []byte
tokenBindingVersion uint16
extendedMasterSecret bool
srtpProtectionProfiles []uint16
srtpMasterKeyIdentifier string
sctListSupported bool
customExtension string
hasGREASEExtension bool
omitExtensions bool
emptyExtensions bool
pad int
compressedCertAlgs []uint16
delegatedCredentials bool
alpsProtocols []string
prefixExtensions []uint16
}
func (m *clientHelloMsg) marshalKeyShares(bb *byteBuilder) {
@ -570,6 +571,12 @@ func (m *clientHelloMsg) marshal() []byte {
body: m.quicTransportParams,
})
}
if len(m.quicTransportParamsLegacy) > 0 {
extensions = append(extensions, extension{
id: extensionQUICTransportParamsLegacy,
body: m.quicTransportParamsLegacy,
})
}
if m.channelIDSupported {
extensions = append(extensions, extension{id: extensionChannelID})
}
@ -985,6 +992,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
}
case extensionQUICTransportParams:
m.quicTransportParams = body
case extensionQUICTransportParamsLegacy:
m.quicTransportParamsLegacy = body
case extensionChannelID:
if len(body) != 0 {
return false
@ -1319,34 +1328,35 @@ func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
}
type serverExtensions struct {
nextProtoNeg bool
nextProtos []string
ocspStapling bool
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
alpnProtocolEmpty bool
duplicateExtension bool
channelIDRequested bool
tokenBindingParams []byte
tokenBindingVersion uint16
extendedMasterSecret bool
srtpProtectionProfile uint16
srtpMasterKeyIdentifier string
sctList []byte
customExtension string
npnAfterAlpn bool
hasKeyShare bool
hasEarlyData bool
keyShare keyShareEntry
supportedVersion uint16
supportedPoints []uint8
supportedCurves []CurveID
quicTransportParams []byte
serverNameAck bool
applicationSettings []byte
hasApplicationSettings bool
echRetryConfigs []byte
nextProtoNeg bool
nextProtos []string
ocspStapling bool
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
alpnProtocolEmpty bool
duplicateExtension bool
channelIDRequested bool
tokenBindingParams []byte
tokenBindingVersion uint16
extendedMasterSecret bool
srtpProtectionProfile uint16
srtpMasterKeyIdentifier string
sctList []byte
customExtension string
npnAfterAlpn bool
hasKeyShare bool
hasEarlyData bool
keyShare keyShareEntry
supportedVersion uint16
supportedPoints []uint8
supportedCurves []CurveID
quicTransportParams []byte
quicTransportParamsLegacy []byte
serverNameAck bool
applicationSettings []byte
hasApplicationSettings bool
echRetryConfigs []byte
}
func (m *serverExtensions) marshal(extensions *byteBuilder) {
@ -1473,6 +1483,11 @@ func (m *serverExtensions) marshal(extensions *byteBuilder) {
params := extensions.addU16LengthPrefixed()
params.addBytes(m.quicTransportParams)
}
if len(m.quicTransportParamsLegacy) > 0 {
extensions.addU16(extensionQUICTransportParamsLegacy)
params := extensions.addU16LengthPrefixed()
params.addBytes(m.quicTransportParamsLegacy)
}
if m.hasEarlyData {
extensions.addU16(extensionEarlyData)
extensions.addBytes([]byte{0, 0})
@ -1594,6 +1609,8 @@ func (m *serverExtensions) unmarshal(data byteReader, version uint16) bool {
}
case extensionQUICTransportParams:
m.quicTransportParams = body
case extensionQUICTransportParamsLegacy:
m.quicTransportParamsLegacy = body
case extensionEarlyData:
if version < VersionTLS13 || len(body) != 0 {
return false

@ -1449,9 +1449,18 @@ func (hs *serverHandshakeState) processClientExtensions(serverExtensions *server
if len(hs.clientHello.quicTransportParams) > 0 {
c.quicTransportParams = hs.clientHello.quicTransportParams
}
if c.config.QUICTransportParamsUseLegacyCodepoint.IncludeStandard() {
serverExtensions.quicTransportParams = c.config.QUICTransportParams
}
if len(hs.clientHello.quicTransportParamsLegacy) > 0 {
c.quicTransportParamsLegacy = hs.clientHello.quicTransportParamsLegacy
}
if c.config.QUICTransportParamsUseLegacyCodepoint.IncludeLegacy() {
serverExtensions.quicTransportParamsLegacy = c.config.QUICTransportParams
}
if c.vers < VersionTLS13 || config.Bugs.NegotiateEMSAtAllVersions {
disableEMS := config.Bugs.NoExtendedMasterSecret
if c.cipherSuite != nil {

@ -533,8 +533,11 @@ type connectionExpectations struct {
// expected to send.
peerCertificate *Certificate
// quicTransportParams contains the QUIC transport parameters that are to be
// sent by the peer.
// sent by the peer using codepoint 57.
quicTransportParams []byte
// quicTransportParamsLegacy contains the QUIC transport parameters that are
// to be sent by the peer using legacy codepoint 0xffa5.
quicTransportParamsLegacy []byte
// peerApplicationSettings are the expected application settings for the
// connection. If nil, no application settings are expected.
peerApplicationSettings []byte
@ -947,6 +950,12 @@ func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, tr
}
}
if len(expectations.quicTransportParamsLegacy) > 0 {
if !bytes.Equal(expectations.quicTransportParamsLegacy, connState.QUICTransportParamsLegacy) {
return errors.New("Peer did not send expected legacy QUIC transport params")
}
}
if test.exportKeyingMaterial > 0 {
actual := make([]byte, test.exportKeyingMaterial)
if _, err := io.ReadFull(tlsConn, actual); err != nil {
@ -1300,20 +1309,25 @@ func runTest(statusChan chan statusMsg, test *testCase, shimPath string, mallocN
flags = append(flags, "-quic")
if !test.skipTransportParamsConfig {
test.config.QUICTransportParams = []byte{1, 2}
test.config.QUICTransportParamsUseLegacyCodepoint = QUICUseCodepointStandard
if test.resumeConfig != nil {
test.resumeConfig.QUICTransportParams = []byte{1, 2}
test.resumeConfig.QUICTransportParamsUseLegacyCodepoint = QUICUseCodepointStandard
}
test.expectations.quicTransportParams = []byte{3, 4}
if test.resumeExpectations != nil {
test.resumeExpectations.quicTransportParams = []byte{3, 4}
}
useCodepointFlag := "0"
if test.config.QUICTransportParamsUseLegacyCodepoint == QUICUseCodepointLegacy {
useCodepointFlag = "1"
}
flags = append(flags,
[]string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
"-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
}...)
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
"-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
"-quic-use-legacy-codepoint", useCodepointFlag)
}
if !test.skipQUICALPNConfig {
flags = append(flags,
@ -7955,118 +7969,169 @@ func addExtensionTests() {
// Test QUIC transport params
if protocol == quic {
// Client sends params
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "QUICTransportParams-Client-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{1, 2},
},
flags: []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
"-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
},
expectations: connectionExpectations{
quicTransportParams: []byte{3, 4},
},
skipTransportParamsConfig: true,
})
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "QUICTransportParams-Client-RejectMissing-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
},
flags: []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
},
shouldFail: true,
expectedError: ":MISSING_EXTENSION:",
skipTransportParamsConfig: true,
})
for _, clientConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
for _, serverSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
useCodepointFlag := "0"
if clientConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
}
flags := []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
"-quic-use-legacy-codepoint", useCodepointFlag,
}
expectations := connectionExpectations{
quicTransportParams: []byte{1, 2},
}
shouldFail := false
expectedError := ""
expectedLocalError := ""
if clientConfig == QUICUseCodepointLegacy {
expectations = connectionExpectations{
quicTransportParamsLegacy: []byte{1, 2},
}
}
if serverSends != clientConfig {
expectations = connectionExpectations{}
shouldFail = true
if serverSends == QUICUseCodepointNeither {
expectedError = ":MISSING_EXTENSION:"
} else {
expectedLocalError = "remote error: unsupported extension"
}
} else {
flags = append(flags,
"-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}))
}
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: fmt.Sprintf("QUICTransportParams-Client-Client%s-Server%s-%s", clientConfig, serverSends, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{3, 4},
QUICTransportParamsUseLegacyCodepoint: serverSends,
},
flags: flags,
expectations: expectations,
shouldFail: shouldFail,
expectedError: expectedError,
expectedLocalError: expectedLocalError,
skipTransportParamsConfig: true,
})
}
}
// Server sends params
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: "QUICTransportParams-Server-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{1, 2},
},
flags: []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
"-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
},
expectations: connectionExpectations{
quicTransportParams: []byte{3, 4},
},
skipTransportParamsConfig: true,
})
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: "QUICTransportParams-Server-RejectMissing-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
},
flags: []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
},
expectations: connectionExpectations{
quicTransportParams: []byte{3, 4},
},
shouldFail: true,
expectedError: ":MISSING_EXTENSION:",
skipTransportParamsConfig: true,
})
for _, clientSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
for _, serverConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
expectations := connectionExpectations{
quicTransportParams: []byte{3, 4},
}
shouldFail := false
expectedError := ""
useCodepointFlag := "0"
if serverConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
expectations = connectionExpectations{
quicTransportParamsLegacy: []byte{3, 4},
}
}
flags := []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
"-quic-use-legacy-codepoint", useCodepointFlag,
}
if clientSends != QUICUseCodepointBoth && clientSends != serverConfig {
expectations = connectionExpectations{}
shouldFail = true
expectedError = ":MISSING_EXTENSION:"
} else {
flags = append(flags,
"-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
)
}
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: fmt.Sprintf("QUICTransportParams-Server-Client%s-Server%s-%s", clientSends, serverConfig, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{1, 2},
QUICTransportParamsUseLegacyCodepoint: clientSends,
},
flags: flags,
expectations: expectations,
shouldFail: shouldFail,
expectedError: expectedError,
skipTransportParamsConfig: true,
})
}
}
} else {
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: "QUICTransportParams-Client-NotSentInNonQUIC-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
},
flags: []string{
"-max-version",
strconv.Itoa(int(ver.versionWire)),
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
},
shouldFail: true,
expectedError: ":QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED:",
skipTransportParamsConfig: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "QUICTransportParams-Server-RejectedInNonQUIC-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{1, 2},
},
flags: []string{
"-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
},
shouldFail: true,
expectedLocalError: "remote error: unsupported extension",
skipTransportParamsConfig: true,
})
// Ensure non-QUIC client doesn't send QUIC transport parameters.
for _, clientConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
useCodepointFlag := "0"
if clientConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("QUICTransportParams-Client-NotSentInNonQUIC-%s-%s", clientConfig, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParamsUseLegacyCodepoint: clientConfig,
},
flags: []string{
"-max-version",
strconv.Itoa(int(ver.versionWire)),
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
"-quic-use-legacy-codepoint", useCodepointFlag,
},
shouldFail: true,
expectedError: ":QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED:",
skipTransportParamsConfig: true,
})
}
// Ensure non-QUIC server rejects codepoint 57 but ignores legacy 0xffa5.
for _, clientSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
for _, serverConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
shouldFail := false
expectedLocalError := ""
useCodepointFlag := "0"
if serverConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
}
if clientSends == QUICUseCodepointStandard || clientSends == QUICUseCodepointBoth {
shouldFail = true
expectedLocalError = "remote error: unsupported extension"
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("QUICTransportParams-NonQUICServer-Client%s-Server%s-%s", clientSends, serverConfig, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{1, 2},
QUICTransportParamsUseLegacyCodepoint: clientSends,
},
flags: []string{
"-quic-use-legacy-codepoint", useCodepointFlag,
},
shouldFail: shouldFail,
expectedLocalError: expectedLocalError,
skipTransportParamsConfig: true,
})
}
}
}
// Test ticket behavior.

@ -230,6 +230,7 @@ const Flag<int> kIntFlags[] = {
{"-max-send-fragment", &TestConfig::max_send_fragment},
{"-read-size", &TestConfig::read_size},
{"-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew},
{"-quic-use-legacy-codepoint", &TestConfig::quic_use_legacy_codepoint},
};
const Flag<std::vector<int>> kIntVectorFlags[] = {
@ -1737,6 +1738,9 @@ bssl::UniquePtr<SSL> TestConfig::NewSSL(
if (max_send_fragment > 0) {
SSL_set_max_send_fragment(ssl.get(), max_send_fragment);
}
if (quic_use_legacy_codepoint != -1) {
SSL_set_quic_use_legacy_codepoint(ssl.get(), quic_use_legacy_codepoint);
}
if (!quic_transport_params.empty()) {
if (!SSL_set_quic_transport_params(
ssl.get(),

@ -74,6 +74,8 @@ struct TestConfig {
std::unique_ptr<std::string> expect_peer_application_settings;
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;

Loading…
Cancel
Save