Selaa lähdekoodia

Implement Token Binding

Update-Note: Token Binding can no longer be configured with the custom
  extensions API. Instead, use the new built-in implementation. (The
  internal repository should be all set.)

Bug: 183

Change-Id: I007523a638dc99582ebd1d177c38619fa7e1ac38
Reviewed-on: https://boringssl-review.googlesource.com/20645
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
kris/onging/CECPQ3_patch15
Nick Harper 7 vuotta sitten
committed by CQ bot account: commit-bot@chromium.org
vanhempi
commit
36fcc4ca5d
19 muutettua tiedostoa jossa 781 lisäystä ja 2 poistoa
  1. +1
    -0
      crypto/err/ssl.errordata
  2. +1
    -1
      include/openssl/base.h
  3. +28
    -0
      include/openssl/ssl.h
  4. +3
    -0
      include/openssl/tls1.h
  5. +7
    -0
      ssl/handshake_client.cc
  6. +17
    -0
      ssl/internal.h
  7. +23
    -0
      ssl/ssl_lib.cc
  8. +164
    -0
      ssl/t1_lib.cc
  9. +18
    -0
      ssl/test/bssl_shim.cc
  10. +17
    -0
      ssl/test/runner/common.go
  11. +5
    -0
      ssl/test/runner/conn.go
  12. +25
    -0
      ssl/test/runner/handshake_client.go
  13. +33
    -0
      ssl/test/runner/handshake_messages.go
  14. +15
    -0
      ssl/test/runner/handshake_server.go
  15. +415
    -0
      ssl/test/runner/runner.go
  16. +3
    -0
      ssl/test/test_config.cc
  17. +2
    -0
      ssl/test/test_config.h
  18. +2
    -1
      ssl/tls13_client.cc
  19. +2
    -0
      ssl/tls13_server.cc

+ 1
- 0
crypto/err/ssl.errordata Näytä tiedosto

@@ -86,6 +86,7 @@ SSL,167,MISSING_TMP_ECDH_KEY
SSL,168,MIXED_SPECIAL_OPERATOR_WITH_GROUPS
SSL,169,MTU_TOO_SMALL
SSL,170,NEGOTIATED_BOTH_NPN_AND_ALPN
SSL,285,NEGOTIATED_TB_WITHOUT_EMS_OR_RI
SSL,171,NESTED_GROUP
SSL,172,NO_CERTIFICATES_RETURNED
SSL,173,NO_CERTIFICATE_ASSIGNED


+ 1
- 1
include/openssl/base.h Näytä tiedosto

@@ -151,7 +151,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 6
#define BORINGSSL_API_VERSION 7

#if defined(BORINGSSL_SHARED_LIBRARY)



+ 28
- 0
include/openssl/ssl.h Näytä tiedosto

@@ -2785,6 +2785,33 @@ OPENSSL_EXPORT void (*SSL_CTX_get_channel_id_cb(SSL_CTX *ctx))(
SSL *ssl, EVP_PKEY **out_pkey);


// Token Binding.
//
// See draft-ietf-tokbind-protocol-16.

// SSL_set_token_binding_params sets |params| as the Token Binding Key
// parameters (section 3 of draft-ietf-tokbind-protocol-16) to negotiate on the
// connection. If this function is not called, or if |len| is 0, then this
// endpoint will not attempt to negotiate Token Binding. |params| are provided
// in preference order, with the more preferred parameters at the beginning of
// the list. This function returns 1 on success and 0 on failure.
OPENSSL_EXPORT int SSL_set_token_binding_params(SSL *ssl, const uint8_t *params,
size_t len);

// SSL_is_token_binding_negotiated returns 1 if Token Binding was negotiated
// on this connection and 0 otherwise. On a server, it is possible for this
// function to return 1 when the client's view of the connection is that Token
// Binding was not negotiated. This occurs when the server indicates a version
// of Token Binding less than the client's minimum version.
OPENSSL_EXPORT int SSL_is_token_binding_negotiated(const SSL *ssl);

// SSL_get_negotiated_token_binding_param returns the TokenBindingKeyParameters
// enum value that was negotiated. It is only valid to call this function if
// SSL_is_token_binding_negotiated returned 1, otherwise this function returns
// an undefined value.
OPENSSL_EXPORT uint8_t SSL_get_negotiated_token_binding_param(const SSL *ssl);


// DTLS-SRTP.
//
// See RFC 5764.
@@ -4588,6 +4615,7 @@ OPENSSL_EXPORT bool SealRecord(SSL *ssl, Span<uint8_t> out_prefix,
#define SSL_R_EMPTY_HELLO_RETRY_REQUEST 282
#define SSL_R_EARLY_DATA_NOT_IN_USE 283
#define SSL_R_HANDSHAKE_NOT_COMPLETE 284
#define SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI 285
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020


+ 3
- 0
include/openssl/tls1.h Näytä tiedosto

@@ -202,6 +202,9 @@ extern "C" {
// ExtensionType value from RFC7627
#define TLSEXT_TYPE_extended_master_secret 23

// ExtensionType value from draft-ietf-tokbind-negotiation-10
#define TLSEXT_TYPE_token_binding 24

// ExtensionType value from RFC4507
#define TLSEXT_TYPE_session_ticket 35



+ 7
- 0
ssl/handshake_client.cc Näytä tiedosto

@@ -757,6 +757,13 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
return ssl_hs_error;
}

if (ssl->token_binding_negotiated &&
(!hs->extended_master_secret || !ssl->s3->send_connection_binding)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
return ssl_hs_error;
}

ssl->method->next_message(ssl);

if (ssl->session != NULL) {


+ 17
- 0
ssl/internal.h Näytä tiedosto

@@ -1376,6 +1376,12 @@ struct SSL_HANDSHAKE {
// peer_key is the peer's ECDH key for a TLS 1.2 client.
Array<uint8_t> peer_key;

// negotiated_token_binding_version is used by a server to store the
// on-the-wire encoding of the Token Binding protocol version to advertise in
// the ServerHello/EncryptedExtensions if the Token Binding extension is to be
// sent.
uint16_t negotiated_token_binding_version;

// server_params, in a TLS 1.2 server, stores the ServerKeyExchange
// parameters. It has client and server randoms prepended for signing
// convenience.
@@ -2606,6 +2612,14 @@ struct SSLConnection {
uint8_t *alpn_client_proto_list;
unsigned alpn_client_proto_list_len;

// Contains a list of supported Token Binding key parameters.
uint8_t *token_binding_params;
size_t token_binding_params_len;

// The negotiated Token Binding key parameter. Only valid if
// |token_binding_negotiated| is set.
uint8_t negotiated_token_binding_param;

// renegotiate_mode controls how peer renegotiation attempts are handled.
enum ssl_renegotiate_mode_t renegotiate_mode;

@@ -2633,6 +2647,9 @@ struct SSLConnection {
// we'll advertise support.
bool tlsext_channel_id_enabled:1;

// token_binding_negotiated is set if Token Binding was negotiated.
bool token_binding_negotiated:1;

// retain_only_sha256_of_client_certs is true if we should compute the SHA256
// hash of the peer's certificate and then discard it to save memory and
// session space. Only effective on the server side.


+ 23
- 0
ssl/ssl_lib.cc Näytä tiedosto

@@ -771,6 +771,7 @@ void SSL_free(SSL *ssl) {
SSL_CTX_free(ssl->session_ctx);
OPENSSL_free(ssl->supported_group_list);
OPENSSL_free(ssl->alpn_client_proto_list);
OPENSSL_free(ssl->token_binding_params);
EVP_PKEY_free(ssl->tlsext_channel_id_private);
OPENSSL_free(ssl->psk_identity_hint);
sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free);
@@ -2122,6 +2123,28 @@ size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out, size_t max_out) {
return 64;
}

int SSL_set_token_binding_params(SSL *ssl, const uint8_t *params, size_t len) {
if (len > 256) {
OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
return 0;
}
OPENSSL_free(ssl->token_binding_params);
ssl->token_binding_params = (uint8_t *)BUF_memdup(params, len);
if (!ssl->token_binding_params) {
return 0;
}
ssl->token_binding_params_len = len;
return 1;
}

int SSL_is_token_binding_negotiated(const SSL *ssl) {
return ssl->token_binding_negotiated;
}

uint8_t SSL_get_negotiated_token_binding_param(const SSL *ssl) {
return ssl->negotiated_token_binding_param;
}

size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) {
if (ssl->server || ssl->s3->hs == NULL) {
*out_types = NULL;


+ 164
- 0
ssl/t1_lib.cc Näytä tiedosto

@@ -2439,6 +2439,153 @@ static bool ext_supported_groups_parse_clienthello(SSL_HANDSHAKE *hs,
return true;
}

// Token Binding
//
// https://tools.ietf.org/html/draft-ietf-tokbind-negotiation-10

// The Token Binding version number currently matches the draft number of
// draft-ietf-tokbind-protocol, and when published as an RFC it will be 0x0100.
// Since there are no wire changes to the protocol from draft 13 through the
// current draft (16), this implementation supports all versions in that range.
static uint16_t kTokenBindingMaxVersion = 16;
static uint16_t kTokenBindingMinVersion = 13;

static bool ext_token_binding_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
if (ssl->token_binding_params == nullptr || SSL_is_dtls(ssl)) {
return true;
}

CBB contents, params;
if (!CBB_add_u16(out, TLSEXT_TYPE_token_binding) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_u16(&contents, kTokenBindingMaxVersion) ||
!CBB_add_u8_length_prefixed(&contents, &params) ||
!CBB_add_bytes(&params, ssl->token_binding_params,
ssl->token_binding_params_len) ||
!CBB_flush(out)) {
return false;
}

return true;
}

static bool ext_token_binding_parse_serverhello(SSL_HANDSHAKE *hs,
uint8_t *out_alert,
CBS *contents) {
SSL *const ssl = hs->ssl;
if (contents == nullptr) {
return true;
}

CBS params_list;
uint16_t version;
uint8_t param;
if (!CBS_get_u16(contents, &version) ||
!CBS_get_u8_length_prefixed(contents, &params_list) ||
!CBS_get_u8(&params_list, &param) ||
CBS_len(&params_list) > 0 ||
CBS_len(contents) > 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}

// The server-negotiated version must be less than or equal to our version.
if (version > kTokenBindingMaxVersion) {
*out_alert = SSL_AD_ILLEGAL_PARAMETER;
return false;
}

// If the server-selected version is less than what we support, then Token
// Binding wasn't negotiated (but the extension was parsed successfully).
if (version < kTokenBindingMinVersion) {
return true;
}

for (size_t i = 0; i < ssl->token_binding_params_len; ++i) {
if (param == ssl->token_binding_params[i]) {
ssl->negotiated_token_binding_param = param;
ssl->token_binding_negotiated = true;
return true;
}
}

*out_alert = SSL_AD_ILLEGAL_PARAMETER;
return false;
}

// select_tb_param looks for the first token binding param in
// |ssl->token_binding_params| that is also in |params| and puts it in
// |ssl->negotiated_token_binding_param|. It returns true if a token binding
// param is found, and false otherwise.
static bool select_tb_param(SSL *ssl, Span<const uint8_t> peer_params) {
for (size_t i = 0; i < ssl->token_binding_params_len; ++i) {
uint8_t tb_param = ssl->token_binding_params[i];
for (uint8_t peer_param : peer_params) {
if (tb_param == peer_param) {
ssl->negotiated_token_binding_param = tb_param;
return true;
}
}
}
return false;
}

static bool ext_token_binding_parse_clienthello(SSL_HANDSHAKE *hs,
uint8_t *out_alert,
CBS *contents) {
SSL *const ssl = hs->ssl;
if (contents == nullptr || ssl->token_binding_params == nullptr) {
return true;
}

CBS params;
uint16_t version;
if (!CBS_get_u16(contents, &version) ||
!CBS_get_u8_length_prefixed(contents, &params) ||
CBS_len(&params) == 0 ||
CBS_len(contents) > 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}

// If the client-selected version is less than what we support, then Token
// Binding wasn't negotiated (but the extension was parsed successfully).
if (version < kTokenBindingMinVersion) {
return true;
}

// If the client-selected version is higher than we support, use our max
// version. Otherwise, use the client's version.
hs->negotiated_token_binding_version =
std::min(version, kTokenBindingMaxVersion);
if (!select_tb_param(ssl, params)) {
return true;
}

ssl->token_binding_negotiated = true;
return true;
}

static bool ext_token_binding_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;

if (!ssl->token_binding_negotiated) {
return true;
}

CBB contents, params;
if (!CBB_add_u16(out, TLSEXT_TYPE_token_binding) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_u16(&contents, hs->negotiated_token_binding_version) ||
!CBB_add_u8_length_prefixed(&contents, &params) ||
!CBB_add_u8(&params, ssl->negotiated_token_binding_param) ||
!CBB_flush(out)) {
return false;
}

return true;
}

// kExtensions contains all the supported extensions.
static const struct tls_extension kExtensions[] = {
@@ -2608,6 +2755,14 @@ static const struct tls_extension kExtensions[] = {
ext_supported_groups_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_token_binding,
NULL,
ext_token_binding_add_clienthello,
ext_token_binding_parse_serverhello,
ext_token_binding_parse_clienthello,
ext_token_binding_add_serverhello,
},
};

#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -2970,6 +3125,15 @@ static int ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,

static int ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;

if (ssl->token_binding_negotiated &&
!(SSL_get_secure_renegotiation_support(ssl) &&
SSL_get_extms_support(ssl))) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
return -1;
}

int ret = SSL_TLSEXT_ERR_NOACK;
int al = SSL_AD_UNRECOGNIZED_NAME;



+ 18
- 0
ssl/test/bssl_shim.cc Näytä tiedosto

@@ -1725,6 +1725,18 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume,
}
}

if (config->expected_token_binding_param != -1) {
if (!SSL_is_token_binding_negotiated(ssl)) {
fprintf(stderr, "no Token Binding negotiated\n");
return false;
}
if (SSL_get_negotiated_token_binding_param(ssl) !=
static_cast<uint8_t>(config->expected_token_binding_param)) {
fprintf(stderr, "Token Binding param mismatch\n");
return false;
}
}

if (config->expect_extended_master_secret && !SSL_get_extms_support(ssl)) {
fprintf(stderr, "No EMS for connection when expected\n");
return false;
@@ -1970,6 +1982,12 @@ static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session,
}
}
}
if (!config->send_token_binding_params.empty()) {
SSL_set_token_binding_params(ssl.get(),
reinterpret_cast<const uint8_t *>(
config->send_token_binding_params.data()),
config->send_token_binding_params.length());
}
if (!config->host_name.empty() &&
!SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) {
return false;


+ 17
- 0
ssl/test/runner/common.go Näytä tiedosto

@@ -122,6 +122,7 @@ const (
extensionSignedCertificateTimestamp uint16 = 18
extensionPadding uint16 = 21
extensionExtendedMasterSecret uint16 = 23
extensionTokenBinding uint16 = 24
extensionSessionTicket uint16 = 35
extensionOldKeyShare uint16 = 40 // draft-ietf-tls-tls13-16
extensionPreSharedKey uint16 = 41 // draft-ietf-tls-tls13-16
@@ -261,6 +262,8 @@ type ConnectionState struct {
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
ChannelID *ecdsa.PublicKey // the channel ID for this connection
TokenBindingNegotiated bool // whether Token Binding was negotiated
TokenBindingParam uint8 // the negotiated Token Binding key parameter
SRTPProtectionProfile uint16 // the negotiated DTLS-SRTP protection profile
TLSUnique []byte // the tls-unique channel binding
SCTList []byte // signed certificate timestamp list
@@ -453,6 +456,20 @@ type Config struct {
// returned in the ConnectionState.
RequestChannelID bool

// TokenBindingParams contains a list of TokenBindingKeyParameters
// (draft-ietf-tokbind-protocol-16) to attempt to negotiate. If
// nil, Token Binding will not be negotiated.
TokenBindingParams []byte

// TokenBindingVersion contains the serialized ProtocolVersion to
// use when negotiating Token Binding.
TokenBindingVersion uint16

// ExpectTokenBindingParams is checked by a server that the client
// sent ExpectTokenBindingParams as its list of Token Binding
// paramters.
ExpectTokenBindingParams []byte

// PreSharedKey, if not nil, is the pre-shared key to use with
// the PSK cipher suites.
PreSharedKey []byte


+ 5
- 0
ssl/test/runner/conn.go Näytä tiedosto

@@ -78,6 +78,9 @@ type Conn struct {

channelID *ecdsa.PublicKey

tokenBindingNegotiated bool
tokenBindingParam uint8

srtpProtectionProfile uint16

clientVersion uint16
@@ -1812,6 +1815,8 @@ func (c *Conn) ConnectionState() ConnectionState {
state.VerifiedChains = c.verifiedChains
state.ServerName = c.serverName
state.ChannelID = c.channelID
state.TokenBindingNegotiated = c.tokenBindingNegotiated
state.TokenBindingParam = c.tokenBindingParam
state.SRTPProtectionProfile = c.srtpProtectionProfile
state.TLSUnique = c.firstFinished[:]
state.SCTList = c.sctList


+ 25
- 0
ssl/test/runner/handshake_client.go Näytä tiedosto

@@ -89,6 +89,8 @@ func (c *Conn) clientHandshake() error {
alpnProtocols: c.config.NextProtos,
duplicateExtension: c.config.Bugs.DuplicateExtension,
channelIDSupported: c.config.ChannelID != nil,
tokenBindingParams: c.config.TokenBindingParams,
tokenBindingVersion: c.config.TokenBindingVersion,
npnAfterAlpn: c.config.Bugs.SwapNPNAndALPN,
extendedMasterSecret: maxVersion >= VersionTLS10,
srtpProtectionProfiles: c.config.SRTPProtectionProfiles,
@@ -1451,6 +1453,29 @@ func (hs *clientHandshakeState) processServerExtensions(serverExtensions *server
return errors.New("server advertised unrequested Channel ID extension")
}

if len(serverExtensions.tokenBindingParams) == 1 {
found := false
for _, p := range c.config.TokenBindingParams {
if p == serverExtensions.tokenBindingParams[0] {
c.tokenBindingParam = p
found = true
break
}
}
if !found {
return errors.New("tls: server advertised unsupported Token Binding key param")
}
if serverExtensions.tokenBindingVersion > c.config.TokenBindingVersion {
return errors.New("tls: server's Token Binding version is too new")
}
if c.vers < VersionTLS13 {
if !serverExtensions.extendedMasterSecret || serverExtensions.secureRenegotiation == nil {
return errors.New("server sent Token Binding without EMS or RI")
}
}
c.tokenBindingNegotiated = true
}

if serverExtensions.extendedMasterSecret && c.vers >= VersionTLS13 {
return errors.New("tls: server advertised extended master secret over TLS 1.3")
}


+ 33
- 0
ssl/test/runner/handshake_messages.go Näytä tiedosto

@@ -281,6 +281,8 @@ type clientHelloMsg struct {
alpnProtocols []string
duplicateExtension bool
channelIDSupported bool
tokenBindingParams []byte
tokenBindingVersion uint16
npnAfterAlpn bool
extendedMasterSecret bool
srtpProtectionProfiles []uint16
@@ -331,6 +333,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
m.duplicateExtension == m1.duplicateExtension &&
m.channelIDSupported == m1.channelIDSupported &&
bytes.Equal(m.tokenBindingParams, m1.tokenBindingParams) &&
m.tokenBindingVersion == m1.tokenBindingVersion &&
m.npnAfterAlpn == m1.npnAfterAlpn &&
m.extendedMasterSecret == m1.extendedMasterSecret &&
eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) &&
@@ -519,6 +523,13 @@ func (m *clientHelloMsg) marshal() []byte {
extensions.addU16(extensionChannelID)
extensions.addU16(0) // Length is always 0
}
if m.tokenBindingParams != nil {
extensions.addU16(extensionTokenBinding)
tokbindExtension := extensions.addU16LengthPrefixed()
tokbindExtension.addU16(m.tokenBindingVersion)
tokbindParams := tokbindExtension.addU8LengthPrefixed()
tokbindParams.addBytes(m.tokenBindingParams)
}
if m.nextProtoNeg && m.npnAfterAlpn {
extensions.addU16(extensionNextProtoNeg)
extensions.addU16(0) // Length is always 0
@@ -826,6 +837,12 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
return false
}
m.channelIDSupported = true
case extensionTokenBinding:
if !body.readU16(&m.tokenBindingVersion) ||
!body.readU8LengthPrefixedBytes(&m.tokenBindingParams) ||
len(body) != 0 {
return false
}
case extensionExtendedMasterSecret:
if len(body) != 0 {
return false
@@ -1116,6 +1133,8 @@ type serverExtensions struct {
alpnProtocolEmpty bool
duplicateExtension bool
channelIDRequested bool
tokenBindingParams []byte
tokenBindingVersion uint16
extendedMasterSecret bool
srtpProtectionProfile uint16
srtpMasterKeyIdentifier string
@@ -1175,6 +1194,13 @@ func (m *serverExtensions) marshal(extensions *byteBuilder) {
extensions.addU16(extensionChannelID)
extensions.addU16(0)
}
if m.tokenBindingParams != nil {
extensions.addU16(extensionTokenBinding)
tokbindExtension := extensions.addU16LengthPrefixed()
tokbindExtension.addU16(m.tokenBindingVersion)
tokbindParams := tokbindExtension.addU8LengthPrefixed()
tokbindParams.addBytes(m.tokenBindingParams)
}
if m.duplicateExtension {
// Add a duplicate bogus extension at the beginning and end.
extensions.addU16(0xffff)
@@ -1303,6 +1329,13 @@ func (m *serverExtensions) unmarshal(data byteReader, version uint16) bool {
return false
}
m.channelIDRequested = true
case extensionTokenBinding:
if !body.readU16(&m.tokenBindingVersion) ||
!body.readU8LengthPrefixedBytes(&m.tokenBindingParams) ||
len(m.tokenBindingParams) != 1 ||
len(body) != 0 {
return false
}
case extensionExtendedMasterSecret:
if len(body) != 0 {
return false


+ 15
- 0
ssl/test/runner/handshake_server.go Näytä tiedosto

@@ -1381,6 +1381,21 @@ func (hs *serverHandshakeState) processClientExtensions(serverExtensions *server
serverExtensions.channelIDRequested = true
}

if config.TokenBindingParams != nil {
if !bytes.Equal(config.ExpectTokenBindingParams, hs.clientHello.tokenBindingParams) {
return errors.New("client did not send expected token binding params")
}

// For testing, blindly send whatever is set in config, even if
// it is invalid.
serverExtensions.tokenBindingParams = config.TokenBindingParams
serverExtensions.tokenBindingVersion = config.TokenBindingVersion
}

if len(hs.clientHello.tokenBindingParams) > 0 && (!hs.clientHello.extendedMasterSecret || hs.clientHello.secureRenegotiation == nil) {
return errors.New("client sent Token Binding without EMS and/or RI")
}

if hs.clientHello.srtpProtectionProfiles != nil {
SRTPLoop:
for _, p1 := range c.config.SRTPProtectionProfiles {


+ 415
- 0
ssl/test/runner/runner.go Näytä tiedosto

@@ -343,6 +343,12 @@ type testCase struct {
// expectChannelID controls whether the connection should have
// negotiated a Channel ID with channelIDKey.
expectChannelID bool
// expectTokenBinding controls whether the connection should have
// negotiated Token Binding.
expectTokenBinding bool
// expectedTokenBindingParam is the Token Binding parameter that should
// have been negotiated (if expectTokenBinding is true).
expectedTokenBindingParam uint8
// expectedNextProto controls whether the connection should
// negotiate a next protocol via NPN or ALPN.
expectedNextProto string
@@ -648,6 +654,17 @@ func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, tr
return fmt.Errorf("channel ID unexpectedly negotiated")
}

if test.expectTokenBinding {
if !connState.TokenBindingNegotiated {
return errors.New("no Token Binding negotiated")
}
if connState.TokenBindingParam != test.expectedTokenBindingParam {
return fmt.Errorf("expected param %02x, but got %02x", test.expectedTokenBindingParam, connState.TokenBindingParam)
}
} else if connState.TokenBindingNegotiated {
return errors.New("Token Binding unexpectedly negotiated")
}

if expected := test.expectedNextProto; expected != "" {
if actual := connState.NegotiatedProtocol; actual != expected {
return fmt.Errorf("next proto mismatch: got %s, wanted %s", actual, expected)
@@ -6149,6 +6166,404 @@ func addExtensionTests() {
})
}

// Test Token Binding.

const maxTokenBindingVersion = 16
const minTokenBindingVersion = 13
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{0, 1, 2},
TokenBindingVersion: maxTokenBindingVersion,
},
expectTokenBinding: true,
expectedTokenBindingParam: 2,
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
"-expected-token-binding-param",
"2",
},
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-UnsupportedParam-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{3},
TokenBindingVersion: maxTokenBindingVersion,
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-OldVersion-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{0, 1, 2},
TokenBindingVersion: minTokenBindingVersion - 1,
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-NewVersion-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{0, 1, 2},
TokenBindingVersion: maxTokenBindingVersion + 1,
},
expectTokenBinding: true,
expectedTokenBindingParam: 2,
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
"-expected-token-binding-param",
"2",
},
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-NoParams-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{},
TokenBindingVersion: maxTokenBindingVersion,
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-RepeatedParam" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{0, 1, 2, 2},
TokenBindingVersion: maxTokenBindingVersion,
},
expectTokenBinding: true,
expectedTokenBindingParam: 2,
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
"-expected-token-binding-param",
"2",
},
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: maxTokenBindingVersion,
ExpectTokenBindingParams: []byte{0, 1, 2},
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
"-expected-token-binding-param",
"2",
},
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-Unexpected-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: maxTokenBindingVersion,
},
tls13Variant: ver.tls13Variant,
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-ExtraParams-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2, 1},
TokenBindingVersion: maxTokenBindingVersion,
ExpectTokenBindingParams: []byte{0, 1, 2},
},
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
"-expected-token-binding-param",
"2",
},
tls13Variant: ver.tls13Variant,
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-NoParams-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{},
TokenBindingVersion: maxTokenBindingVersion,
ExpectTokenBindingParams: []byte{0, 1, 2},
},
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
"-expected-token-binding-param",
"2",
},
tls13Variant: ver.tls13Variant,
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-WrongParam-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{3},
TokenBindingVersion: maxTokenBindingVersion,
ExpectTokenBindingParams: []byte{0, 1, 2},
},
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
"-expected-token-binding-param",
"2",
},
tls13Variant: ver.tls13Variant,
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-OldVersion-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: minTokenBindingVersion - 1,
ExpectTokenBindingParams: []byte{0, 1, 2},
},
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
},
tls13Variant: ver.tls13Variant,
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-MinVersion-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: minTokenBindingVersion,
ExpectTokenBindingParams: []byte{0, 1, 2},
},
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
"-expected-token-binding-param",
"2",
},
tls13Variant: ver.tls13Variant,
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-VersionTooNew-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: maxTokenBindingVersion + 1,
ExpectTokenBindingParams: []byte{0, 1, 2},
},
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
},
tls13Variant: ver.tls13Variant,
shouldFail: true,
expectedError: "ERROR_PARSING_EXTENSION",
})
if ver.version < VersionTLS13 {
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-NoEMS-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: maxTokenBindingVersion,
ExpectTokenBindingParams: []byte{2, 1, 0},
Bugs: ProtocolBugs{
NoExtendedMasterSecret: true,
},
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
shouldFail: true,
expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-NoEMS-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{0, 1, 2},
TokenBindingVersion: maxTokenBindingVersion,
Bugs: ProtocolBugs{
NoExtendedMasterSecret: true,
},
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
shouldFail: true,
expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-Client-NoRI-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: maxTokenBindingVersion,
ExpectTokenBindingParams: []byte{2, 1, 0},
Bugs: ProtocolBugs{
NoRenegotiationInfo: true,
},
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
shouldFail: true,
expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-Server-NoRI-" + ver.name,

config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{0, 1, 2},
TokenBindingVersion: maxTokenBindingVersion,
Bugs: ProtocolBugs{
NoRenegotiationInfo: true,
},
},
tls13Variant: ver.tls13Variant,
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
shouldFail: true,
expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
})
} else {
testCases = append(testCases, testCase{
testType: clientTest,
name: "TokenBinding-WithEarlyDataFails-" + ver.name,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{2},
TokenBindingVersion: maxTokenBindingVersion,
ExpectTokenBindingParams: []byte{2, 1, 0},
MaxEarlyDataSize: 16384,
},
resumeSession: true,
tls13Variant: ver.tls13Variant,
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "TokenBinding-EarlyDataRejected-" + ver.name,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
TokenBindingParams: []byte{0, 1, 2},
TokenBindingVersion: maxTokenBindingVersion,
MaxEarlyDataSize: 16384,
},
resumeSession: true,
expectTokenBinding: true,
expectedTokenBindingParam: 2,
tls13Variant: ver.tls13Variant,
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
},
})
}

// Test ticket behavior.

// Resume with a corrupt ticket.


+ 3
- 0
ssl/test/test_config.cc Näytä tiedosto

@@ -163,6 +163,7 @@ const Flag<std::string> kStringFlags[] = {
const Flag<std::string> kBase64Flags[] = {
{ "-expect-certificate-types", &TestConfig::expected_certificate_types },
{ "-expect-channel-id", &TestConfig::expected_channel_id },
{ "-token-binding-params", &TestConfig::send_token_binding_params },
{ "-expect-ocsp-response", &TestConfig::expected_ocsp_response },
{ "-expect-signed-cert-timestamps",
&TestConfig::expected_signed_cert_timestamps },
@@ -174,6 +175,8 @@ const Flag<std::string> kBase64Flags[] = {
const Flag<int> kIntFlags[] = {
{ "-port", &TestConfig::port },
{ "-resume-count", &TestConfig::resume_count },
{ "-expected-token-binding-param",
&TestConfig::expected_token_binding_param },
{ "-min-version", &TestConfig::min_version },
{ "-max-version", &TestConfig::max_version },
{ "-expect-version", &TestConfig::expect_version },


+ 2
- 0
ssl/test/test_config.h Näytä tiedosto

@@ -49,6 +49,8 @@ struct TestConfig {
std::string expected_channel_id;
bool enable_channel_id = false;
std::string send_channel_id;
int expected_token_binding_param = -1;
std::string send_token_binding_params;
bool shim_writes_first = false;
std::string host_name;
std::string advertise_alpn;


+ 2
- 1
ssl/tls13_client.cc Näytä tiedosto

@@ -476,7 +476,8 @@ static enum ssl_hs_wait_t do_read_encrypted_extensions(SSL_HANDSHAKE *hs) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ALPN_MISMATCH_ON_EARLY_DATA);
return ssl_hs_error;
}
if (ssl->s3->tlsext_channel_id_valid || hs->received_custom_extension) {
if (ssl->s3->tlsext_channel_id_valid || hs->received_custom_extension ||
ssl->token_binding_negotiated) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION_ON_EARLY_DATA);
return ssl_hs_error;
}


+ 2
- 0
ssl/tls13_server.cc Näytä tiedosto

@@ -398,6 +398,8 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) {
hs->early_data_offered &&
// Channel ID is incompatible with 0-RTT.
!ssl->s3->tlsext_channel_id_valid &&
// If Token Binding is negotiated, reject 0-RTT.
!ssl->token_binding_negotiated &&
// Custom extensions is incompatible with 0-RTT.
hs->custom_extensions.received == 0 &&
// The negotiated ALPN must match the one in the ticket.


Ladataan…
Peruuta
Tallenna