From 6222fe767dd911bc2dac62b7cbf170c5b73dee8d Mon Sep 17 00:00:00 2001 From: Dan McArdle Date: Mon, 28 Sep 2020 12:50:18 -0400 Subject: [PATCH] runner: Refactor BoGo clientHelloMsg extension marshalling. This CL replaces clientHelloMsg's npnAfterAlpn and pskBinderFirst fields with a new field: prefixExtensions. The extensions in prefixExtensions are tried first when marshalling clientHelloMsg. The ability to control extensions' marshalling order will make it simpler to implement the "outer_extensions" behavior defined in draft-ietf-tls-esni-07. Bug: 275 Change-Id: Ib6dcc1e6fa0281f312cb65a9e204415c3f3ef2c6 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43064 Commit-Queue: David Benjamin Reviewed-by: David Benjamin --- ssl/test/runner/common.go | 1 + ssl/test/runner/handshake_client.go | 20 ++- ssl/test/runner/handshake_messages.go | 241 ++++++++++++++++---------- 3 files changed, 167 insertions(+), 95 deletions(-) diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index d1ea08df8..b7517e71e 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -125,6 +125,7 @@ const ( extensionQUICTransportParams uint16 = 0xffa5 // draft-ietf-quic-tls-13 extensionChannelID uint16 = 30032 // not IANA assigned extensionDelegatedCredentials uint16 = 0x22 // draft-ietf-tls-subcerts-06 + extensionDuplicate uint16 = 0xffff // not IANA assigned ) // TLS signaling cipher suite values diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 8548b7889..241518fed 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -100,6 +100,19 @@ func (c *Conn) clientHandshake() error { return errors.New("tls: NextProtos values too large") } + // Translate the bugs that modify ClientHello extension order into a + // list of prefix extensions. The marshal function will try these + // extensions before any others, followed by any remaining extensions in + // the default order. + var prefixExtensions []uint16 + if c.config.Bugs.PSKBinderFirst && !c.config.Bugs.OnlyCorruptSecondPSKBinder { + prefixExtensions = append(prefixExtensions, extensionPreSharedKey) + } + if c.config.Bugs.SwapNPNAndALPN { + prefixExtensions = append(prefixExtensions, extensionALPN) + prefixExtensions = append(prefixExtensions, extensionNextProtoNeg) + } + minVersion := c.config.minVersion(c.isDTLS) maxVersion := c.config.maxVersion(c.isDTLS) hello := &clientHelloMsg{ @@ -119,15 +132,14 @@ func (c *Conn) clientHandshake() error { 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, srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer, customExtension: c.config.Bugs.CustomExtension, - pskBinderFirst: c.config.Bugs.PSKBinderFirst && !c.config.Bugs.OnlyCorruptSecondPSKBinder, omitExtensions: c.config.Bugs.OmitExtensions, emptyExtensions: c.config.Bugs.EmptyExtensions, delegatedCredentials: !c.config.Bugs.DisableDelegatedCredentials, + prefixExtensions: prefixExtensions, } if maxVersion >= VersionTLS13 { @@ -608,7 +620,9 @@ NextCipherSuite: hello.hasEarlyData = c.config.Bugs.SendEarlyDataOnSecondClientHello // The first ClientHello may have skipped this due to OnlyCorruptSecondPSKBinder. - hello.pskBinderFirst = c.config.Bugs.PSKBinderFirst + if c.config.Bugs.PSKBinderFirst && c.config.Bugs.OnlyCorruptSecondPSKBinder { + hello.prefixExtensions = append(hello.prefixExtensions, extensionPreSharedKey) + } if c.config.Bugs.OmitPSKsOnSecondClientHello { hello.pskIdentities = nil hello.pskBinders = nil diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 9d3b6bcbd..500e14ee2 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -285,19 +285,18 @@ type clientHelloMsg struct { channelIDSupported bool tokenBindingParams []byte tokenBindingVersion uint16 - npnAfterAlpn bool extendedMasterSecret bool srtpProtectionProfiles []uint16 srtpMasterKeyIdentifier string sctListSupported bool customExtension string hasGREASEExtension bool - pskBinderFirst bool omitExtensions bool emptyExtensions bool pad int compressedCertAlgs []uint16 delegatedCredentials bool + prefixExtensions []uint16 } func (m *clientHelloMsg) equal(i interface{}) bool { @@ -340,19 +339,18 @@ func (m *clientHelloMsg) equal(i interface{}) bool { 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) && m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier && m.sctListSupported == m1.sctListSupported && m.customExtension == m1.customExtension && m.hasGREASEExtension == m1.hasGREASEExtension && - m.pskBinderFirst == m1.pskBinderFirst && m.omitExtensions == m1.omitExtensions && m.emptyExtensions == m1.emptyExtensions && m.pad == m1.pad && eqUint16s(m.compressedCertAlgs, m1.compressedCertAlgs) && - m.delegatedCredentials == m1.delegatedCredentials + m.delegatedCredentials == m1.delegatedCredentials && + eqUint16s(m.prefixExtensions, m1.prefixExtensions) } func (m *clientHelloMsg) marshalKeyShares(bb *byteBuilder) { @@ -390,34 +388,20 @@ func (m *clientHelloMsg) marshal() []byte { compressionMethods := hello.addU8LengthPrefixed() compressionMethods.addBytes(m.compressionMethods) - extensions := hello.addU16LengthPrefixed() - if len(m.pskIdentities) > 0 && m.pskBinderFirst { - extensions.addU16(extensionPreSharedKey) - pskExtension := extensions.addU16LengthPrefixed() - - pskIdentities := pskExtension.addU16LengthPrefixed() - for _, psk := range m.pskIdentities { - pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket) - pskIdentities.addU32(psk.obfuscatedTicketAge) - } - pskBinders := pskExtension.addU16LengthPrefixed() - for _, binder := range m.pskBinders { - pskBinders.addU8LengthPrefixed().addBytes(binder) - } + type extension struct { + id uint16 + body []byte } + var extensions []extension + if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - extensions.addU16(0xffff) - extensions.addU16(0) // 0-length for empty extension + extensions = append(extensions, extension{id: extensionDuplicate}) } - if m.nextProtoNeg && !m.npnAfterAlpn { - extensions.addU16(extensionNextProtoNeg) - extensions.addU16(0) // The length is always 0 + if m.nextProtoNeg { + extensions = append(extensions, extension{id: extensionNextProtoNeg}) } if len(m.serverName) > 0 { - extensions.addU16(extensionServerName) - serverNameList := extensions.addU16LengthPrefixed() - // RFC 3546, section 3.1 // // struct { @@ -437,138 +421,170 @@ func (m *clientHelloMsg) marshal() []byte { // ServerName server_name_list<1..2^16-1> // } ServerNameList; + serverNameList := newByteBuilder() serverName := serverNameList.addU16LengthPrefixed() serverName.addU8(0) // NameType host_name(0) hostName := serverName.addU16LengthPrefixed() hostName.addBytes([]byte(m.serverName)) + + extensions = append(extensions, extension{ + id: extensionServerName, + body: serverNameList.finish(), + }) } if m.ocspStapling { - extensions.addU16(extensionStatusRequest) - certificateStatusRequest := extensions.addU16LengthPrefixed() - + certificateStatusRequest := newByteBuilder() // RFC 4366, section 3.6 certificateStatusRequest.addU8(1) // OCSP type // Two zero valued uint16s for the two lengths. certificateStatusRequest.addU16(0) // ResponderID length certificateStatusRequest.addU16(0) // Extensions length + extensions = append(extensions, extension{ + id: extensionStatusRequest, + body: certificateStatusRequest.finish(), + }) } if len(m.supportedCurves) > 0 { // http://tools.ietf.org/html/rfc4492#section-5.1.1 - extensions.addU16(extensionSupportedCurves) - supportedCurvesList := extensions.addU16LengthPrefixed() + supportedCurvesList := newByteBuilder() supportedCurves := supportedCurvesList.addU16LengthPrefixed() for _, curve := range m.supportedCurves { supportedCurves.addU16(uint16(curve)) } + extensions = append(extensions, extension{ + id: extensionSupportedCurves, + body: supportedCurvesList.finish(), + }) } if len(m.supportedPoints) > 0 { // http://tools.ietf.org/html/rfc4492#section-5.1.2 - extensions.addU16(extensionSupportedPoints) - supportedPointsList := extensions.addU16LengthPrefixed() + supportedPointsList := newByteBuilder() supportedPoints := supportedPointsList.addU8LengthPrefixed() supportedPoints.addBytes(m.supportedPoints) + extensions = append(extensions, extension{ + id: extensionSupportedPoints, + body: supportedPointsList.finish(), + }) } if m.hasKeyShares { - extensions.addU16(extensionKeyShare) - keyShareList := extensions.addU16LengthPrefixed() + keyShareList := newByteBuilder() m.marshalKeyShares(keyShareList) + extensions = append(extensions, extension{ + id: extensionKeyShare, + body: keyShareList.finish(), + }) } if len(m.pskKEModes) > 0 { - extensions.addU16(extensionPSKKeyExchangeModes) - pskModesExtension := extensions.addU16LengthPrefixed() + pskModesExtension := newByteBuilder() pskModesExtension.addU8LengthPrefixed().addBytes(m.pskKEModes) + extensions = append(extensions, extension{ + id: extensionPSKKeyExchangeModes, + body: pskModesExtension.finish(), + }) } if m.hasEarlyData { - extensions.addU16(extensionEarlyData) - extensions.addU16(0) // The length is zero. + extensions = append(extensions, extension{id: extensionEarlyData}) } if len(m.tls13Cookie) > 0 { - extensions.addU16(extensionCookie) - body := extensions.addU16LengthPrefixed() + body := newByteBuilder() body.addU16LengthPrefixed().addBytes(m.tls13Cookie) + extensions = append(extensions, extension{ + id: extensionCookie, + body: body.finish(), + }) } if m.ticketSupported { // http://tools.ietf.org/html/rfc5077#section-3.2 - extensions.addU16(extensionSessionTicket) - sessionTicketExtension := extensions.addU16LengthPrefixed() - sessionTicketExtension.addBytes(m.sessionTicket) + extensions = append(extensions, extension{ + id: extensionSessionTicket, + body: m.sessionTicket, + }) } if len(m.signatureAlgorithms) > 0 { // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 - extensions.addU16(extensionSignatureAlgorithms) - signatureAlgorithmsExtension := extensions.addU16LengthPrefixed() + signatureAlgorithmsExtension := newByteBuilder() signatureAlgorithms := signatureAlgorithmsExtension.addU16LengthPrefixed() for _, sigAlg := range m.signatureAlgorithms { signatureAlgorithms.addU16(uint16(sigAlg)) } + extensions = append(extensions, extension{ + id: extensionSignatureAlgorithms, + body: signatureAlgorithmsExtension.finish(), + }) } if len(m.signatureAlgorithmsCert) > 0 { - extensions.addU16(extensionSignatureAlgorithmsCert) - signatureAlgorithmsCertExtension := extensions.addU16LengthPrefixed() + signatureAlgorithmsCertExtension := newByteBuilder() signatureAlgorithmsCert := signatureAlgorithmsCertExtension.addU16LengthPrefixed() for _, sigAlg := range m.signatureAlgorithmsCert { signatureAlgorithmsCert.addU16(uint16(sigAlg)) } + extensions = append(extensions, extension{ + id: extensionSignatureAlgorithmsCert, + body: signatureAlgorithmsCertExtension.finish(), + }) } if len(m.supportedVersions) > 0 { - extensions.addU16(extensionSupportedVersions) - supportedVersionsExtension := extensions.addU16LengthPrefixed() + supportedVersionsExtension := newByteBuilder() supportedVersions := supportedVersionsExtension.addU8LengthPrefixed() for _, version := range m.supportedVersions { supportedVersions.addU16(uint16(version)) } + extensions = append(extensions, extension{ + id: extensionSupportedVersions, + body: supportedVersionsExtension.finish(), + }) } if m.secureRenegotiation != nil { - extensions.addU16(extensionRenegotiationInfo) - secureRenegoExt := extensions.addU16LengthPrefixed() - secureRenego := secureRenegoExt.addU8LengthPrefixed() - secureRenego.addBytes(m.secureRenegotiation) + secureRenegoExt := newByteBuilder() + secureRenegoExt.addU8LengthPrefixed().addBytes(m.secureRenegotiation) + extensions = append(extensions, extension{ + id: extensionRenegotiationInfo, + body: secureRenegoExt.finish(), + }) } if len(m.alpnProtocols) > 0 { // https://tools.ietf.org/html/rfc7301#section-3.1 - extensions.addU16(extensionALPN) - alpnExtension := extensions.addU16LengthPrefixed() - + alpnExtension := newByteBuilder() protocolNameList := alpnExtension.addU16LengthPrefixed() for _, s := range m.alpnProtocols { protocolName := protocolNameList.addU8LengthPrefixed() protocolName.addBytes([]byte(s)) } + extensions = append(extensions, extension{ + id: extensionALPN, + body: alpnExtension.finish(), + }) } if len(m.quicTransportParams) > 0 { - extensions.addU16(extensionQUICTransportParams) - params := extensions.addU16LengthPrefixed() - params.addBytes(m.quicTransportParams) + extensions = append(extensions, extension{ + id: extensionQUICTransportParams, + body: m.quicTransportParams, + }) } if m.channelIDSupported { - extensions.addU16(extensionChannelID) - extensions.addU16(0) // Length is always 0 + extensions = append(extensions, extension{id: extensionChannelID}) } if m.tokenBindingParams != nil { - extensions.addU16(extensionTokenBinding) - tokbindExtension := extensions.addU16LengthPrefixed() + tokbindExtension := newByteBuilder() 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 + extensions = append(extensions, extension{ + id: extensionTokenBinding, + body: tokbindExtension.finish(), + }) } if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - extensions.addU16(0xffff) - extensions.addU16(0) + extensions = append(extensions, extension{id: extensionDuplicate}) } if m.extendedMasterSecret { // https://tools.ietf.org/html/rfc7627 - extensions.addU16(extensionExtendedMasterSecret) - extensions.addU16(0) // Length is always 0 + extensions = append(extensions, extension{id: extensionExtendedMasterSecret}) } if len(m.srtpProtectionProfiles) > 0 { // https://tools.ietf.org/html/rfc5764#section-4.1.1 - extensions.addU16(extensionUseSRTP) - useSrtpExt := extensions.addU16LengthPrefixed() + useSrtpExt := newByteBuilder() srtpProtectionProfiles := useSrtpExt.addU16LengthPrefixed() for _, p := range m.srtpProtectionProfiles { @@ -576,38 +592,47 @@ func (m *clientHelloMsg) marshal() []byte { } srtpMki := useSrtpExt.addU8LengthPrefixed() srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier)) + + extensions = append(extensions, extension{ + id: extensionUseSRTP, + body: useSrtpExt.finish(), + }) } if m.sctListSupported { - extensions.addU16(extensionSignedCertificateTimestamp) - extensions.addU16(0) // Length is always 0 + extensions = append(extensions, extension{id: extensionSignedCertificateTimestamp}) } if l := len(m.customExtension); l > 0 { - extensions.addU16(extensionCustom) - customExt := extensions.addU16LengthPrefixed() - customExt.addBytes([]byte(m.customExtension)) + extensions = append(extensions, extension{ + id: extensionCustom, + body: []byte(m.customExtension), + }) } if len(m.compressedCertAlgs) > 0 { - extensions.addU16(extensionCompressedCertAlgs) - body := extensions.addU16LengthPrefixed() + body := newByteBuilder() algIDs := body.addU8LengthPrefixed() for _, v := range m.compressedCertAlgs { algIDs.addU16(v) } + extensions = append(extensions, extension{ + id: extensionCompressedCertAlgs, + body: body.finish(), + }) } if m.delegatedCredentials { - extensions.addU16(extensionDelegatedCredentials) - body := extensions.addU16LengthPrefixed() + body := newByteBuilder() signatureSchemeList := body.addU16LengthPrefixed() for _, sigAlg := range m.signatureAlgorithms { signatureSchemeList.addU16(uint16(sigAlg)) } + extensions = append(extensions, extension{ + id: extensionDelegatedCredentials, + body: body.finish(), + }) } // The PSK extension must be last. See https://tools.ietf.org/html/rfc8446#section-4.2.11 - if len(m.pskIdentities) > 0 && !m.pskBinderFirst { - extensions.addU16(extensionPreSharedKey) - pskExtension := extensions.addU16LengthPrefixed() - + if len(m.pskIdentities) > 0 { + pskExtension := newByteBuilder() pskIdentities := pskExtension.addU16LengthPrefixed() for _, psk := range m.pskIdentities { pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket) @@ -617,11 +642,43 @@ func (m *clientHelloMsg) marshal() []byte { for _, binder := range m.pskBinders { pskBinders.addU8LengthPrefixed().addBytes(binder) } + extensions = append(extensions, extension{ + id: extensionPreSharedKey, + body: pskExtension.finish(), + }) + } + + // Write each extension in |extensions| to the |hello| message, hoisting + // the extensions named in |m.prefixExtensions| to the front. + extensionsBB := hello.addU16LengthPrefixed() + extMap := make(map[uint16][]byte) + for _, ext := range extensions { + extMap[ext.id] = ext.body + } + // Write each of the prefix extensions, if we have it. + for _, extID := range m.prefixExtensions { + if body, ok := extMap[extID]; ok { + extensionsBB.addU16(extID) + extensionsBB.addU16LengthPrefixed().addBytes(body) + } + } + // Forget each of the prefix extensions. This loop is separate from the + // extension-writing loop because |m.prefixExtensions| may contain + // duplicates. + for _, extID := range m.prefixExtensions { + delete(extMap, extID) + } + // Write each of the remaining extensions in their original order. + for _, ext := range extensions { + if _, ok := extMap[ext.id]; ok { + extensionsBB.addU16(ext.id) + extensionsBB.addU16LengthPrefixed().addBytes(ext.body) + } } if m.pad != 0 && hello.len()%m.pad != 0 { - extensions.addU16(extensionPadding) - padding := extensions.addU16LengthPrefixed() + extensionsBB.addU16(extensionPadding) + padding := extensionsBB.addU16LengthPrefixed() // Note hello.len() has changed at this point from the length // prefix. if l := hello.len() % m.pad; l != 0 { @@ -1236,7 +1293,7 @@ type serverExtensions struct { func (m *serverExtensions) marshal(extensions *byteBuilder) { if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - extensions.addU16(0xffff) + extensions.addU16(extensionDuplicate) extensions.addU16(0) // length = 0 for empty extension } if m.nextProtoNeg && !m.npnAfterAlpn { @@ -1286,7 +1343,7 @@ func (m *serverExtensions) marshal(extensions *byteBuilder) { } if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - extensions.addU16(0xffff) + extensions.addU16(extensionDuplicate) extensions.addU16(0) } if m.extendedMasterSecret {