9c33ae8562
08b65f4e31
introduced a memory leak and
also got enums confused. Also fix a codepath that was missing an error
code.
Thanks to OSS-Fuzz which appears to have found it in a matter of hours.
Change-Id: Ia9e926c28a01daab3e6154d363d0acda91209a22
Reviewed-on: https://boringssl-review.googlesource.com/13104
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
2387 lines
55 KiB
Go
2387 lines
55 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package runner
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
)
|
|
|
|
func writeLen(buf []byte, v, size int) {
|
|
for i := 0; i < size; i++ {
|
|
buf[size-i-1] = byte(v)
|
|
v >>= 8
|
|
}
|
|
if v != 0 {
|
|
panic("length is too long")
|
|
}
|
|
}
|
|
|
|
type byteBuilder struct {
|
|
buf *[]byte
|
|
start int
|
|
prefixLen int
|
|
child *byteBuilder
|
|
}
|
|
|
|
func newByteBuilder() *byteBuilder {
|
|
buf := make([]byte, 0, 32)
|
|
return &byteBuilder{buf: &buf}
|
|
}
|
|
|
|
func (bb *byteBuilder) len() int {
|
|
return len(*bb.buf) - bb.start - bb.prefixLen
|
|
}
|
|
|
|
func (bb *byteBuilder) flush() {
|
|
if bb.child == nil {
|
|
return
|
|
}
|
|
bb.child.flush()
|
|
writeLen((*bb.buf)[bb.child.start:], bb.child.len(), bb.child.prefixLen)
|
|
bb.child = nil
|
|
return
|
|
}
|
|
|
|
func (bb *byteBuilder) finish() []byte {
|
|
bb.flush()
|
|
return *bb.buf
|
|
}
|
|
|
|
func (bb *byteBuilder) addU8(u uint8) {
|
|
bb.flush()
|
|
*bb.buf = append(*bb.buf, u)
|
|
}
|
|
|
|
func (bb *byteBuilder) addU16(u uint16) {
|
|
bb.flush()
|
|
*bb.buf = append(*bb.buf, byte(u>>8), byte(u))
|
|
}
|
|
|
|
func (bb *byteBuilder) addU24(u int) {
|
|
bb.flush()
|
|
*bb.buf = append(*bb.buf, byte(u>>16), byte(u>>8), byte(u))
|
|
}
|
|
|
|
func (bb *byteBuilder) addU32(u uint32) {
|
|
bb.flush()
|
|
*bb.buf = append(*bb.buf, byte(u>>24), byte(u>>16), byte(u>>8), byte(u))
|
|
}
|
|
|
|
func (bb *byteBuilder) addU64(u uint64) {
|
|
bb.flush()
|
|
var b [8]byte
|
|
binary.BigEndian.PutUint64(b[:], u)
|
|
*bb.buf = append(*bb.buf, b[:]...)
|
|
}
|
|
|
|
func (bb *byteBuilder) addU8LengthPrefixed() *byteBuilder {
|
|
return bb.createChild(1)
|
|
}
|
|
|
|
func (bb *byteBuilder) addU16LengthPrefixed() *byteBuilder {
|
|
return bb.createChild(2)
|
|
}
|
|
|
|
func (bb *byteBuilder) addU24LengthPrefixed() *byteBuilder {
|
|
return bb.createChild(3)
|
|
}
|
|
|
|
func (bb *byteBuilder) addU32LengthPrefixed() *byteBuilder {
|
|
return bb.createChild(4)
|
|
}
|
|
|
|
func (bb *byteBuilder) addBytes(b []byte) {
|
|
bb.flush()
|
|
*bb.buf = append(*bb.buf, b...)
|
|
}
|
|
|
|
func (bb *byteBuilder) createChild(lengthPrefixSize int) *byteBuilder {
|
|
bb.flush()
|
|
bb.child = &byteBuilder{
|
|
buf: bb.buf,
|
|
start: len(*bb.buf),
|
|
prefixLen: lengthPrefixSize,
|
|
}
|
|
for i := 0; i < lengthPrefixSize; i++ {
|
|
*bb.buf = append(*bb.buf, 0)
|
|
}
|
|
return bb.child
|
|
}
|
|
|
|
func (bb *byteBuilder) discardChild() {
|
|
if bb.child != nil {
|
|
return
|
|
}
|
|
bb.child = nil
|
|
*bb.buf = (*bb.buf)[:bb.start]
|
|
}
|
|
|
|
type keyShareEntry struct {
|
|
group CurveID
|
|
keyExchange []byte
|
|
}
|
|
|
|
type pskIdentity struct {
|
|
ticket []uint8
|
|
obfuscatedTicketAge uint32
|
|
}
|
|
|
|
type clientHelloMsg struct {
|
|
raw []byte
|
|
isDTLS bool
|
|
vers uint16
|
|
random []byte
|
|
sessionId []byte
|
|
cookie []byte
|
|
cipherSuites []uint16
|
|
compressionMethods []uint8
|
|
nextProtoNeg bool
|
|
serverName string
|
|
ocspStapling bool
|
|
supportedCurves []CurveID
|
|
supportedPoints []uint8
|
|
hasKeyShares bool
|
|
keyShares []keyShareEntry
|
|
trailingKeyShareData bool
|
|
pskIdentities []pskIdentity
|
|
pskKEModes []byte
|
|
pskBinders [][]uint8
|
|
hasEarlyData bool
|
|
tls13Cookie []byte
|
|
ticketSupported bool
|
|
sessionTicket []uint8
|
|
signatureAlgorithms []signatureAlgorithm
|
|
supportedVersions []uint16
|
|
secureRenegotiation []byte
|
|
alpnProtocols []string
|
|
duplicateExtension bool
|
|
channelIDSupported bool
|
|
npnAfterAlpn bool
|
|
extendedMasterSecret bool
|
|
srtpProtectionProfiles []uint16
|
|
srtpMasterKeyIdentifier string
|
|
sctListSupported bool
|
|
customExtension string
|
|
hasGREASEExtension bool
|
|
pskBinderFirst bool
|
|
shortHeaderSupported bool
|
|
}
|
|
|
|
func (m *clientHelloMsg) equal(i interface{}) bool {
|
|
m1, ok := i.(*clientHelloMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
m.isDTLS == m1.isDTLS &&
|
|
m.vers == m1.vers &&
|
|
bytes.Equal(m.random, m1.random) &&
|
|
bytes.Equal(m.sessionId, m1.sessionId) &&
|
|
bytes.Equal(m.cookie, m1.cookie) &&
|
|
eqUint16s(m.cipherSuites, m1.cipherSuites) &&
|
|
bytes.Equal(m.compressionMethods, m1.compressionMethods) &&
|
|
m.nextProtoNeg == m1.nextProtoNeg &&
|
|
m.serverName == m1.serverName &&
|
|
m.ocspStapling == m1.ocspStapling &&
|
|
eqCurveIDs(m.supportedCurves, m1.supportedCurves) &&
|
|
bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
|
|
m.hasKeyShares == m1.hasKeyShares &&
|
|
eqKeyShareEntryLists(m.keyShares, m1.keyShares) &&
|
|
m.trailingKeyShareData == m1.trailingKeyShareData &&
|
|
eqPSKIdentityLists(m.pskIdentities, m1.pskIdentities) &&
|
|
bytes.Equal(m.pskKEModes, m1.pskKEModes) &&
|
|
eqByteSlices(m.pskBinders, m1.pskBinders) &&
|
|
m.hasEarlyData == m1.hasEarlyData &&
|
|
bytes.Equal(m.tls13Cookie, m1.tls13Cookie) &&
|
|
m.ticketSupported == m1.ticketSupported &&
|
|
bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
|
|
eqSignatureAlgorithms(m.signatureAlgorithms, m1.signatureAlgorithms) &&
|
|
eqUint16s(m.supportedVersions, m1.supportedVersions) &&
|
|
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
|
|
(m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) &&
|
|
eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
|
|
m.duplicateExtension == m1.duplicateExtension &&
|
|
m.channelIDSupported == m1.channelIDSupported &&
|
|
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.shortHeaderSupported == m1.shortHeaderSupported
|
|
}
|
|
|
|
func (m *clientHelloMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
handshakeMsg := newByteBuilder()
|
|
handshakeMsg.addU8(typeClientHello)
|
|
hello := handshakeMsg.addU24LengthPrefixed()
|
|
hello.addU16(m.vers)
|
|
hello.addBytes(m.random)
|
|
sessionId := hello.addU8LengthPrefixed()
|
|
sessionId.addBytes(m.sessionId)
|
|
if m.isDTLS {
|
|
cookie := hello.addU8LengthPrefixed()
|
|
cookie.addBytes(m.cookie)
|
|
}
|
|
cipherSuites := hello.addU16LengthPrefixed()
|
|
for _, suite := range m.cipherSuites {
|
|
cipherSuites.addU16(suite)
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
if m.duplicateExtension {
|
|
// Add a duplicate bogus extension at the beginning and end.
|
|
extensions.addU16(0xffff)
|
|
extensions.addU16(0) // 0-length for empty extension
|
|
}
|
|
if m.nextProtoNeg && !m.npnAfterAlpn {
|
|
extensions.addU16(extensionNextProtoNeg)
|
|
extensions.addU16(0) // The length is always 0
|
|
}
|
|
if len(m.serverName) > 0 {
|
|
extensions.addU16(extensionServerName)
|
|
serverNameList := extensions.addU16LengthPrefixed()
|
|
|
|
// RFC 3546, section 3.1
|
|
//
|
|
// struct {
|
|
// NameType name_type;
|
|
// select (name_type) {
|
|
// case host_name: HostName;
|
|
// } name;
|
|
// } ServerName;
|
|
//
|
|
// enum {
|
|
// host_name(0), (255)
|
|
// } NameType;
|
|
//
|
|
// opaque HostName<1..2^16-1>;
|
|
//
|
|
// struct {
|
|
// ServerName server_name_list<1..2^16-1>
|
|
// } ServerNameList;
|
|
|
|
serverName := serverNameList.addU16LengthPrefixed()
|
|
serverName.addU8(0) // NameType host_name(0)
|
|
hostName := serverName.addU16LengthPrefixed()
|
|
hostName.addBytes([]byte(m.serverName))
|
|
}
|
|
if m.ocspStapling {
|
|
extensions.addU16(extensionStatusRequest)
|
|
certificateStatusRequest := extensions.addU16LengthPrefixed()
|
|
|
|
// 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
|
|
}
|
|
if len(m.supportedCurves) > 0 {
|
|
// http://tools.ietf.org/html/rfc4492#section-5.1.1
|
|
extensions.addU16(extensionSupportedCurves)
|
|
supportedCurvesList := extensions.addU16LengthPrefixed()
|
|
supportedCurves := supportedCurvesList.addU16LengthPrefixed()
|
|
for _, curve := range m.supportedCurves {
|
|
supportedCurves.addU16(uint16(curve))
|
|
}
|
|
}
|
|
if len(m.supportedPoints) > 0 {
|
|
// http://tools.ietf.org/html/rfc4492#section-5.1.2
|
|
extensions.addU16(extensionSupportedPoints)
|
|
supportedPointsList := extensions.addU16LengthPrefixed()
|
|
supportedPoints := supportedPointsList.addU8LengthPrefixed()
|
|
supportedPoints.addBytes(m.supportedPoints)
|
|
}
|
|
if m.hasKeyShares {
|
|
extensions.addU16(extensionKeyShare)
|
|
keyShareList := extensions.addU16LengthPrefixed()
|
|
|
|
keyShares := keyShareList.addU16LengthPrefixed()
|
|
for _, keyShare := range m.keyShares {
|
|
keyShares.addU16(uint16(keyShare.group))
|
|
keyExchange := keyShares.addU16LengthPrefixed()
|
|
keyExchange.addBytes(keyShare.keyExchange)
|
|
}
|
|
|
|
if m.trailingKeyShareData {
|
|
keyShares.addU8(0)
|
|
}
|
|
}
|
|
if len(m.pskKEModes) > 0 {
|
|
extensions.addU16(extensionPSKKeyExchangeModes)
|
|
pskModesExtension := extensions.addU16LengthPrefixed()
|
|
pskModesExtension.addU8LengthPrefixed().addBytes(m.pskKEModes)
|
|
}
|
|
if m.hasEarlyData {
|
|
extensions.addU16(extensionEarlyData)
|
|
extensions.addU16(0) // The length is zero.
|
|
}
|
|
if len(m.tls13Cookie) > 0 {
|
|
extensions.addU16(extensionCookie)
|
|
body := extensions.addU16LengthPrefixed()
|
|
body.addU16LengthPrefixed().addBytes(m.tls13Cookie)
|
|
}
|
|
if m.ticketSupported {
|
|
// http://tools.ietf.org/html/rfc5077#section-3.2
|
|
extensions.addU16(extensionSessionTicket)
|
|
sessionTicketExtension := extensions.addU16LengthPrefixed()
|
|
sessionTicketExtension.addBytes(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()
|
|
signatureAlgorithms := signatureAlgorithmsExtension.addU16LengthPrefixed()
|
|
for _, sigAlg := range m.signatureAlgorithms {
|
|
signatureAlgorithms.addU16(uint16(sigAlg))
|
|
}
|
|
}
|
|
if len(m.supportedVersions) > 0 {
|
|
extensions.addU16(extensionSupportedVersions)
|
|
supportedVersionsExtension := extensions.addU16LengthPrefixed()
|
|
supportedVersions := supportedVersionsExtension.addU8LengthPrefixed()
|
|
for _, version := range m.supportedVersions {
|
|
supportedVersions.addU16(uint16(version))
|
|
}
|
|
}
|
|
if m.secureRenegotiation != nil {
|
|
extensions.addU16(extensionRenegotiationInfo)
|
|
secureRenegoExt := extensions.addU16LengthPrefixed()
|
|
secureRenego := secureRenegoExt.addU8LengthPrefixed()
|
|
secureRenego.addBytes(m.secureRenegotiation)
|
|
}
|
|
if len(m.alpnProtocols) > 0 {
|
|
// https://tools.ietf.org/html/rfc7301#section-3.1
|
|
extensions.addU16(extensionALPN)
|
|
alpnExtension := extensions.addU16LengthPrefixed()
|
|
|
|
protocolNameList := alpnExtension.addU16LengthPrefixed()
|
|
for _, s := range m.alpnProtocols {
|
|
protocolName := protocolNameList.addU8LengthPrefixed()
|
|
protocolName.addBytes([]byte(s))
|
|
}
|
|
}
|
|
if m.channelIDSupported {
|
|
extensions.addU16(extensionChannelID)
|
|
extensions.addU16(0) // Length is always 0
|
|
}
|
|
if m.nextProtoNeg && m.npnAfterAlpn {
|
|
extensions.addU16(extensionNextProtoNeg)
|
|
extensions.addU16(0) // Length is always 0
|
|
}
|
|
if m.duplicateExtension {
|
|
// Add a duplicate bogus extension at the beginning and end.
|
|
extensions.addU16(0xffff)
|
|
extensions.addU16(0)
|
|
}
|
|
if m.extendedMasterSecret {
|
|
// https://tools.ietf.org/html/rfc7627
|
|
extensions.addU16(extensionExtendedMasterSecret)
|
|
extensions.addU16(0) // Length is always 0
|
|
}
|
|
if len(m.srtpProtectionProfiles) > 0 {
|
|
// https://tools.ietf.org/html/rfc5764#section-4.1.1
|
|
extensions.addU16(extensionUseSRTP)
|
|
useSrtpExt := extensions.addU16LengthPrefixed()
|
|
|
|
srtpProtectionProfiles := useSrtpExt.addU16LengthPrefixed()
|
|
for _, p := range m.srtpProtectionProfiles {
|
|
// An SRTPProtectionProfile is defined as uint8[2],
|
|
// not uint16. For some reason, we're storing it
|
|
// as a uint16.
|
|
srtpProtectionProfiles.addU8(byte(p >> 8))
|
|
srtpProtectionProfiles.addU8(byte(p))
|
|
}
|
|
srtpMki := useSrtpExt.addU8LengthPrefixed()
|
|
srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier))
|
|
}
|
|
if m.sctListSupported {
|
|
extensions.addU16(extensionSignedCertificateTimestamp)
|
|
extensions.addU16(0) // Length is always 0
|
|
}
|
|
if l := len(m.customExtension); l > 0 {
|
|
extensions.addU16(extensionCustom)
|
|
customExt := extensions.addU16LengthPrefixed()
|
|
customExt.addBytes([]byte(m.customExtension))
|
|
}
|
|
if m.shortHeaderSupported {
|
|
extensions.addU16(extensionShortHeader)
|
|
extensions.addU16(0) // Length is always 0
|
|
}
|
|
// The PSK extension must be last (draft-ietf-tls-tls13-18 section 4.2.6).
|
|
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)
|
|
}
|
|
}
|
|
|
|
if extensions.len() == 0 {
|
|
hello.discardChild()
|
|
}
|
|
|
|
m.raw = handshakeMsg.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *clientHelloMsg) unmarshal(data []byte) bool {
|
|
if len(data) < 42 {
|
|
return false
|
|
}
|
|
m.raw = data
|
|
m.vers = uint16(data[4])<<8 | uint16(data[5])
|
|
m.random = data[6:38]
|
|
sessionIdLen := int(data[38])
|
|
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
|
|
return false
|
|
}
|
|
m.sessionId = data[39 : 39+sessionIdLen]
|
|
data = data[39+sessionIdLen:]
|
|
if m.isDTLS {
|
|
if len(data) < 1 {
|
|
return false
|
|
}
|
|
cookieLen := int(data[0])
|
|
if cookieLen > 32 || len(data) < 1+cookieLen {
|
|
return false
|
|
}
|
|
m.cookie = data[1 : 1+cookieLen]
|
|
data = data[1+cookieLen:]
|
|
}
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
|
|
// they are uint16s, the number must be even.
|
|
cipherSuiteLen := int(data[0])<<8 | int(data[1])
|
|
if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
|
|
return false
|
|
}
|
|
numCipherSuites := cipherSuiteLen / 2
|
|
m.cipherSuites = make([]uint16, numCipherSuites)
|
|
for i := 0; i < numCipherSuites; i++ {
|
|
m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
|
|
if m.cipherSuites[i] == scsvRenegotiation {
|
|
m.secureRenegotiation = []byte{}
|
|
}
|
|
}
|
|
data = data[2+cipherSuiteLen:]
|
|
if len(data) < 1 {
|
|
return false
|
|
}
|
|
compressionMethodsLen := int(data[0])
|
|
if len(data) < 1+compressionMethodsLen {
|
|
return false
|
|
}
|
|
m.compressionMethods = data[1 : 1+compressionMethodsLen]
|
|
|
|
data = data[1+compressionMethodsLen:]
|
|
|
|
m.nextProtoNeg = false
|
|
m.serverName = ""
|
|
m.ocspStapling = false
|
|
m.keyShares = nil
|
|
m.pskIdentities = nil
|
|
m.hasEarlyData = false
|
|
m.ticketSupported = false
|
|
m.sessionTicket = nil
|
|
m.signatureAlgorithms = nil
|
|
m.supportedVersions = nil
|
|
m.alpnProtocols = nil
|
|
m.extendedMasterSecret = false
|
|
m.customExtension = ""
|
|
|
|
if len(data) == 0 {
|
|
// ClientHello is optionally followed by extension data
|
|
return true
|
|
}
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
|
|
extensionsLength := int(data[0])<<8 | int(data[1])
|
|
data = data[2:]
|
|
if extensionsLength != len(data) {
|
|
return false
|
|
}
|
|
|
|
for len(data) != 0 {
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
extension := uint16(data[0])<<8 | uint16(data[1])
|
|
length := int(data[2])<<8 | int(data[3])
|
|
data = data[4:]
|
|
if len(data) < length {
|
|
return false
|
|
}
|
|
|
|
switch extension {
|
|
case extensionServerName:
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
numNames := int(data[0])<<8 | int(data[1])
|
|
d := data[2:]
|
|
for i := 0; i < numNames; i++ {
|
|
if len(d) < 3 {
|
|
return false
|
|
}
|
|
nameType := d[0]
|
|
nameLen := int(d[1])<<8 | int(d[2])
|
|
d = d[3:]
|
|
if len(d) < nameLen {
|
|
return false
|
|
}
|
|
if nameType == 0 {
|
|
m.serverName = string(d[0:nameLen])
|
|
break
|
|
}
|
|
d = d[nameLen:]
|
|
}
|
|
case extensionNextProtoNeg:
|
|
if length > 0 {
|
|
return false
|
|
}
|
|
m.nextProtoNeg = true
|
|
case extensionStatusRequest:
|
|
m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
|
|
case extensionSupportedCurves:
|
|
// http://tools.ietf.org/html/rfc4492#section-5.5.1
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
l := int(data[0])<<8 | int(data[1])
|
|
if l%2 == 1 || length != l+2 {
|
|
return false
|
|
}
|
|
numCurves := l / 2
|
|
m.supportedCurves = make([]CurveID, numCurves)
|
|
d := data[2:]
|
|
for i := 0; i < numCurves; i++ {
|
|
m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
|
|
d = d[2:]
|
|
}
|
|
case extensionSupportedPoints:
|
|
// http://tools.ietf.org/html/rfc4492#section-5.5.2
|
|
if length < 1 {
|
|
return false
|
|
}
|
|
l := int(data[0])
|
|
if length != l+1 {
|
|
return false
|
|
}
|
|
m.supportedPoints = data[1 : 1+l]
|
|
case extensionSessionTicket:
|
|
// http://tools.ietf.org/html/rfc5077#section-3.2
|
|
m.ticketSupported = true
|
|
m.sessionTicket = data[:length]
|
|
case extensionKeyShare:
|
|
// draft-ietf-tls-tls13 section 6.3.2.3
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
l := int(data[0])<<8 | int(data[1])
|
|
if l != length-2 {
|
|
return false
|
|
}
|
|
d := data[2:length]
|
|
m.hasKeyShares = true
|
|
for len(d) > 0 {
|
|
// The next KeyShareEntry contains a NamedGroup (2 bytes) and a
|
|
// key_exchange (2-byte length prefix with at least 1 byte of content).
|
|
if len(d) < 5 {
|
|
return false
|
|
}
|
|
entry := keyShareEntry{}
|
|
entry.group = CurveID(d[0])<<8 | CurveID(d[1])
|
|
keyExchLen := int(d[2])<<8 | int(d[3])
|
|
d = d[4:]
|
|
if len(d) < keyExchLen {
|
|
return false
|
|
}
|
|
entry.keyExchange = d[:keyExchLen]
|
|
d = d[keyExchLen:]
|
|
m.keyShares = append(m.keyShares, entry)
|
|
}
|
|
case extensionPreSharedKey:
|
|
// draft-ietf-tls-tls13-18 section 4.2.6
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
l := int(data[0])<<8 | int(data[1])
|
|
d := data[2 : l+2]
|
|
// Parse PSK identities.
|
|
for len(d) > 0 {
|
|
if len(d) < 2 {
|
|
return false
|
|
}
|
|
pskLen := int(d[0])<<8 | int(d[1])
|
|
d = d[2:]
|
|
|
|
if len(d) < pskLen+4 {
|
|
return false
|
|
}
|
|
ticket := d[:pskLen]
|
|
obfuscatedTicketAge := uint32(d[pskLen])<<24 | uint32(d[pskLen+1])<<16 | uint32(d[pskLen+2])<<8 | uint32(d[pskLen+3])
|
|
psk := pskIdentity{
|
|
ticket: ticket,
|
|
obfuscatedTicketAge: obfuscatedTicketAge,
|
|
}
|
|
m.pskIdentities = append(m.pskIdentities, psk)
|
|
d = d[pskLen+4:]
|
|
}
|
|
d = data[l+2:]
|
|
if len(d) < 2 {
|
|
return false
|
|
}
|
|
l = int(d[0])<<8 | int(d[1])
|
|
d = d[2:]
|
|
if l != len(d) {
|
|
return false
|
|
}
|
|
// Parse PSK binders.
|
|
for len(d) > 0 {
|
|
if len(d) < 1 {
|
|
return false
|
|
}
|
|
binderLen := int(d[0])
|
|
d = d[1:]
|
|
if binderLen > len(d) {
|
|
return false
|
|
}
|
|
m.pskBinders = append(m.pskBinders, d[:binderLen])
|
|
d = d[binderLen:]
|
|
}
|
|
|
|
// There must be the same number of identities as binders.
|
|
if len(m.pskIdentities) != len(m.pskBinders) {
|
|
return false
|
|
}
|
|
case extensionPSKKeyExchangeModes:
|
|
// draft-ietf-tls-tls13-18 section 4.2.7
|
|
if length < 1 {
|
|
return false
|
|
}
|
|
l := int(data[0])
|
|
if l != length-1 {
|
|
return false
|
|
}
|
|
m.pskKEModes = data[1:length]
|
|
case extensionEarlyData:
|
|
// draft-ietf-tls-tls13 section 6.3.2.5
|
|
if length != 0 {
|
|
return false
|
|
}
|
|
m.hasEarlyData = true
|
|
case extensionCookie:
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
l := int(data[0])<<8 | int(data[1])
|
|
if l != length-2 || l == 0 {
|
|
return false
|
|
}
|
|
m.tls13Cookie = data[2 : 2+l]
|
|
case extensionSignatureAlgorithms:
|
|
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
|
if length < 2 || length&1 != 0 {
|
|
return false
|
|
}
|
|
l := int(data[0])<<8 | int(data[1])
|
|
if l != length-2 {
|
|
return false
|
|
}
|
|
n := l / 2
|
|
d := data[2:]
|
|
m.signatureAlgorithms = make([]signatureAlgorithm, n)
|
|
for i := range m.signatureAlgorithms {
|
|
m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1])
|
|
d = d[2:]
|
|
}
|
|
case extensionSupportedVersions:
|
|
if length < 1+2 {
|
|
return false
|
|
}
|
|
l := int(data[0])
|
|
if l != length-1 || l%2 == 1 || l < 2 {
|
|
return false
|
|
}
|
|
n := l / 2
|
|
d := data[1:]
|
|
m.supportedVersions = make([]uint16, n)
|
|
for i := range m.supportedVersions {
|
|
m.supportedVersions[i] = uint16(d[0])<<8 | uint16(d[1])
|
|
d = d[2:]
|
|
}
|
|
case extensionRenegotiationInfo:
|
|
if length < 1 || length != int(data[0])+1 {
|
|
return false
|
|
}
|
|
m.secureRenegotiation = data[1:length]
|
|
case extensionALPN:
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
l := int(data[0])<<8 | int(data[1])
|
|
if l != length-2 {
|
|
return false
|
|
}
|
|
d := data[2:length]
|
|
for len(d) != 0 {
|
|
stringLen := int(d[0])
|
|
d = d[1:]
|
|
if stringLen == 0 || stringLen > len(d) {
|
|
return false
|
|
}
|
|
m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen]))
|
|
d = d[stringLen:]
|
|
}
|
|
case extensionChannelID:
|
|
if length > 0 {
|
|
return false
|
|
}
|
|
m.channelIDSupported = true
|
|
case extensionExtendedMasterSecret:
|
|
if length != 0 {
|
|
return false
|
|
}
|
|
m.extendedMasterSecret = true
|
|
case extensionUseSRTP:
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
l := int(data[0])<<8 | int(data[1])
|
|
if l > length-2 || l%2 != 0 {
|
|
return false
|
|
}
|
|
n := l / 2
|
|
m.srtpProtectionProfiles = make([]uint16, n)
|
|
d := data[2:length]
|
|
for i := 0; i < n; i++ {
|
|
m.srtpProtectionProfiles[i] = uint16(d[0])<<8 | uint16(d[1])
|
|
d = d[2:]
|
|
}
|
|
if len(d) < 1 || int(d[0]) != len(d)-1 {
|
|
return false
|
|
}
|
|
m.srtpMasterKeyIdentifier = string(d[1:])
|
|
case extensionSignedCertificateTimestamp:
|
|
if length != 0 {
|
|
return false
|
|
}
|
|
m.sctListSupported = true
|
|
case extensionShortHeader:
|
|
if length != 0 {
|
|
return false
|
|
}
|
|
m.shortHeaderSupported = true
|
|
case extensionCustom:
|
|
m.customExtension = string(data[:length])
|
|
}
|
|
data = data[length:]
|
|
|
|
if isGREASEValue(extension) {
|
|
m.hasGREASEExtension = true
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type serverHelloMsg struct {
|
|
raw []byte
|
|
isDTLS bool
|
|
vers uint16
|
|
versOverride uint16
|
|
random []byte
|
|
sessionId []byte
|
|
cipherSuite uint16
|
|
hasKeyShare bool
|
|
keyShare keyShareEntry
|
|
hasPSKIdentity bool
|
|
pskIdentity uint16
|
|
earlyDataIndication bool
|
|
compressionMethod uint8
|
|
customExtension string
|
|
unencryptedALPN string
|
|
shortHeader bool
|
|
extensions serverExtensions
|
|
}
|
|
|
|
func (m *serverHelloMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
handshakeMsg := newByteBuilder()
|
|
handshakeMsg.addU8(typeServerHello)
|
|
hello := handshakeMsg.addU24LengthPrefixed()
|
|
|
|
// m.vers is used both to determine the format of the rest of the
|
|
// ServerHello and to override the value, so include a second version
|
|
// field.
|
|
vers, ok := wireToVersion(m.vers, m.isDTLS)
|
|
if !ok {
|
|
panic("unknown version")
|
|
}
|
|
if m.versOverride != 0 {
|
|
hello.addU16(m.versOverride)
|
|
} else {
|
|
hello.addU16(m.vers)
|
|
}
|
|
|
|
hello.addBytes(m.random)
|
|
if vers < VersionTLS13 {
|
|
sessionId := hello.addU8LengthPrefixed()
|
|
sessionId.addBytes(m.sessionId)
|
|
}
|
|
hello.addU16(m.cipherSuite)
|
|
if vers < VersionTLS13 {
|
|
hello.addU8(m.compressionMethod)
|
|
}
|
|
|
|
extensions := hello.addU16LengthPrefixed()
|
|
|
|
if m.shortHeader {
|
|
extensions.addU16(extensionShortHeader)
|
|
extensions.addU16(0) // Length
|
|
}
|
|
|
|
if vers >= VersionTLS13 {
|
|
if m.hasKeyShare {
|
|
extensions.addU16(extensionKeyShare)
|
|
keyShare := extensions.addU16LengthPrefixed()
|
|
keyShare.addU16(uint16(m.keyShare.group))
|
|
keyExchange := keyShare.addU16LengthPrefixed()
|
|
keyExchange.addBytes(m.keyShare.keyExchange)
|
|
}
|
|
if m.hasPSKIdentity {
|
|
extensions.addU16(extensionPreSharedKey)
|
|
extensions.addU16(2) // Length
|
|
extensions.addU16(m.pskIdentity)
|
|
}
|
|
if m.earlyDataIndication {
|
|
extensions.addU16(extensionEarlyData)
|
|
extensions.addU16(0) // Length
|
|
}
|
|
if len(m.customExtension) > 0 {
|
|
extensions.addU16(extensionCustom)
|
|
customExt := extensions.addU16LengthPrefixed()
|
|
customExt.addBytes([]byte(m.customExtension))
|
|
}
|
|
if len(m.unencryptedALPN) > 0 {
|
|
extensions.addU16(extensionALPN)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
|
|
protocolNameList := extension.addU16LengthPrefixed()
|
|
protocolName := protocolNameList.addU8LengthPrefixed()
|
|
protocolName.addBytes([]byte(m.unencryptedALPN))
|
|
}
|
|
} else {
|
|
m.extensions.marshal(extensions)
|
|
if extensions.len() == 0 {
|
|
hello.discardChild()
|
|
}
|
|
}
|
|
|
|
m.raw = handshakeMsg.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *serverHelloMsg) unmarshal(data []byte) bool {
|
|
if len(data) < 42 {
|
|
return false
|
|
}
|
|
m.raw = data
|
|
m.vers = uint16(data[4])<<8 | uint16(data[5])
|
|
vers, ok := wireToVersion(m.vers, m.isDTLS)
|
|
if !ok {
|
|
return false
|
|
}
|
|
m.random = data[6:38]
|
|
data = data[38:]
|
|
if vers < VersionTLS13 {
|
|
sessionIdLen := int(data[0])
|
|
if sessionIdLen > 32 || len(data) < 1+sessionIdLen {
|
|
return false
|
|
}
|
|
m.sessionId = data[1 : 1+sessionIdLen]
|
|
data = data[1+sessionIdLen:]
|
|
}
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
|
|
data = data[2:]
|
|
if vers < VersionTLS13 {
|
|
if len(data) < 1 {
|
|
return false
|
|
}
|
|
m.compressionMethod = data[0]
|
|
data = data[1:]
|
|
}
|
|
|
|
if len(data) == 0 && m.vers < VersionTLS13 {
|
|
// Extension data is optional before TLS 1.3.
|
|
m.extensions = serverExtensions{}
|
|
return true
|
|
}
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
|
|
extensionsLength := int(data[0])<<8 | int(data[1])
|
|
data = data[2:]
|
|
if len(data) != extensionsLength {
|
|
return false
|
|
}
|
|
|
|
if vers >= VersionTLS13 {
|
|
for len(data) != 0 {
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
extension := uint16(data[0])<<8 | uint16(data[1])
|
|
length := int(data[2])<<8 | int(data[3])
|
|
data = data[4:]
|
|
|
|
if len(data) < length {
|
|
return false
|
|
}
|
|
d := data[:length]
|
|
data = data[length:]
|
|
|
|
switch extension {
|
|
case extensionKeyShare:
|
|
m.hasKeyShare = true
|
|
if len(d) < 4 {
|
|
return false
|
|
}
|
|
m.keyShare.group = CurveID(uint16(d[0])<<8 | uint16(d[1]))
|
|
keyExchLen := int(d[2])<<8 | int(d[3])
|
|
if keyExchLen != len(d)-4 {
|
|
return false
|
|
}
|
|
m.keyShare.keyExchange = make([]byte, keyExchLen)
|
|
copy(m.keyShare.keyExchange, d[4:])
|
|
case extensionPreSharedKey:
|
|
if len(d) != 2 {
|
|
return false
|
|
}
|
|
m.pskIdentity = uint16(d[0])<<8 | uint16(d[1])
|
|
m.hasPSKIdentity = true
|
|
case extensionEarlyData:
|
|
if len(d) != 0 {
|
|
return false
|
|
}
|
|
m.earlyDataIndication = true
|
|
case extensionShortHeader:
|
|
if len(d) != 0 {
|
|
return false
|
|
}
|
|
m.shortHeader = true
|
|
default:
|
|
// Only allow the 3 extensions that are sent in
|
|
// the clear in TLS 1.3.
|
|
return false
|
|
}
|
|
}
|
|
} else if !m.extensions.unmarshal(data, vers) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type encryptedExtensionsMsg struct {
|
|
raw []byte
|
|
extensions serverExtensions
|
|
empty bool
|
|
}
|
|
|
|
func (m *encryptedExtensionsMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
encryptedExtensionsMsg := newByteBuilder()
|
|
encryptedExtensionsMsg.addU8(typeEncryptedExtensions)
|
|
encryptedExtensions := encryptedExtensionsMsg.addU24LengthPrefixed()
|
|
if !m.empty {
|
|
extensions := encryptedExtensions.addU16LengthPrefixed()
|
|
m.extensions.marshal(extensions)
|
|
}
|
|
|
|
m.raw = encryptedExtensionsMsg.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
if len(data) < 6 {
|
|
return false
|
|
}
|
|
if data[0] != typeEncryptedExtensions {
|
|
return false
|
|
}
|
|
msgLen := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
|
data = data[4:]
|
|
if len(data) != msgLen {
|
|
return false
|
|
}
|
|
extLen := int(data[0])<<8 | int(data[1])
|
|
data = data[2:]
|
|
if extLen != len(data) {
|
|
return false
|
|
}
|
|
return m.extensions.unmarshal(data, VersionTLS13)
|
|
}
|
|
|
|
type serverExtensions struct {
|
|
nextProtoNeg bool
|
|
nextProtos []string
|
|
ocspStapling bool
|
|
ticketSupported bool
|
|
secureRenegotiation []byte
|
|
alpnProtocol string
|
|
alpnProtocolEmpty bool
|
|
duplicateExtension bool
|
|
channelIDRequested bool
|
|
extendedMasterSecret bool
|
|
srtpProtectionProfile uint16
|
|
srtpMasterKeyIdentifier string
|
|
sctList []byte
|
|
customExtension string
|
|
npnAfterAlpn bool
|
|
hasKeyShare bool
|
|
keyShare keyShareEntry
|
|
supportedPoints []uint8
|
|
}
|
|
|
|
func (m *serverExtensions) marshal(extensions *byteBuilder) {
|
|
if m.duplicateExtension {
|
|
// Add a duplicate bogus extension at the beginning and end.
|
|
extensions.addU16(0xffff)
|
|
extensions.addU16(0) // length = 0 for empty extension
|
|
}
|
|
if m.nextProtoNeg && !m.npnAfterAlpn {
|
|
extensions.addU16(extensionNextProtoNeg)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
|
|
for _, v := range m.nextProtos {
|
|
if len(v) > 255 {
|
|
v = v[:255]
|
|
}
|
|
npn := extension.addU8LengthPrefixed()
|
|
npn.addBytes([]byte(v))
|
|
}
|
|
}
|
|
if m.ocspStapling {
|
|
extensions.addU16(extensionStatusRequest)
|
|
extensions.addU16(0)
|
|
}
|
|
if m.ticketSupported {
|
|
extensions.addU16(extensionSessionTicket)
|
|
extensions.addU16(0)
|
|
}
|
|
if m.secureRenegotiation != nil {
|
|
extensions.addU16(extensionRenegotiationInfo)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
secureRenego := extension.addU8LengthPrefixed()
|
|
secureRenego.addBytes(m.secureRenegotiation)
|
|
}
|
|
if len(m.alpnProtocol) > 0 || m.alpnProtocolEmpty {
|
|
extensions.addU16(extensionALPN)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
|
|
protocolNameList := extension.addU16LengthPrefixed()
|
|
protocolName := protocolNameList.addU8LengthPrefixed()
|
|
protocolName.addBytes([]byte(m.alpnProtocol))
|
|
}
|
|
if m.channelIDRequested {
|
|
extensions.addU16(extensionChannelID)
|
|
extensions.addU16(0)
|
|
}
|
|
if m.duplicateExtension {
|
|
// Add a duplicate bogus extension at the beginning and end.
|
|
extensions.addU16(0xffff)
|
|
extensions.addU16(0)
|
|
}
|
|
if m.extendedMasterSecret {
|
|
extensions.addU16(extensionExtendedMasterSecret)
|
|
extensions.addU16(0)
|
|
}
|
|
if m.srtpProtectionProfile != 0 {
|
|
extensions.addU16(extensionUseSRTP)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
|
|
srtpProtectionProfiles := extension.addU16LengthPrefixed()
|
|
srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile >> 8))
|
|
srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile))
|
|
srtpMki := extension.addU8LengthPrefixed()
|
|
srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier))
|
|
}
|
|
if m.sctList != nil {
|
|
extensions.addU16(extensionSignedCertificateTimestamp)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
extension.addBytes(m.sctList)
|
|
}
|
|
if l := len(m.customExtension); l > 0 {
|
|
extensions.addU16(extensionCustom)
|
|
customExt := extensions.addU16LengthPrefixed()
|
|
customExt.addBytes([]byte(m.customExtension))
|
|
}
|
|
if m.nextProtoNeg && m.npnAfterAlpn {
|
|
extensions.addU16(extensionNextProtoNeg)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
|
|
for _, v := range m.nextProtos {
|
|
if len(v) > 255 {
|
|
v = v[0:255]
|
|
}
|
|
npn := extension.addU8LengthPrefixed()
|
|
npn.addBytes([]byte(v))
|
|
}
|
|
}
|
|
if m.hasKeyShare {
|
|
extensions.addU16(extensionKeyShare)
|
|
keyShare := extensions.addU16LengthPrefixed()
|
|
keyShare.addU16(uint16(m.keyShare.group))
|
|
keyExchange := keyShare.addU16LengthPrefixed()
|
|
keyExchange.addBytes(m.keyShare.keyExchange)
|
|
}
|
|
if len(m.supportedPoints) > 0 {
|
|
// http://tools.ietf.org/html/rfc4492#section-5.1.2
|
|
extensions.addU16(extensionSupportedPoints)
|
|
supportedPointsList := extensions.addU16LengthPrefixed()
|
|
supportedPoints := supportedPointsList.addU8LengthPrefixed()
|
|
supportedPoints.addBytes(m.supportedPoints)
|
|
}
|
|
}
|
|
|
|
func (m *serverExtensions) unmarshal(data []byte, version uint16) bool {
|
|
// Reset all fields.
|
|
*m = serverExtensions{}
|
|
|
|
for len(data) != 0 {
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
extension := uint16(data[0])<<8 | uint16(data[1])
|
|
length := int(data[2])<<8 | int(data[3])
|
|
data = data[4:]
|
|
if len(data) < length {
|
|
return false
|
|
}
|
|
|
|
switch extension {
|
|
case extensionNextProtoNeg:
|
|
m.nextProtoNeg = true
|
|
d := data[:length]
|
|
for len(d) > 0 {
|
|
l := int(d[0])
|
|
d = d[1:]
|
|
if l == 0 || l > len(d) {
|
|
return false
|
|
}
|
|
m.nextProtos = append(m.nextProtos, string(d[:l]))
|
|
d = d[l:]
|
|
}
|
|
case extensionStatusRequest:
|
|
if length > 0 {
|
|
return false
|
|
}
|
|
m.ocspStapling = true
|
|
case extensionSessionTicket:
|
|
if length > 0 {
|
|
return false
|
|
}
|
|
m.ticketSupported = true
|
|
case extensionRenegotiationInfo:
|
|
if length < 1 || length != int(data[0])+1 {
|
|
return false
|
|
}
|
|
m.secureRenegotiation = data[1:length]
|
|
case extensionALPN:
|
|
d := data[:length]
|
|
if len(d) < 3 {
|
|
return false
|
|
}
|
|
l := int(d[0])<<8 | int(d[1])
|
|
if l != len(d)-2 {
|
|
return false
|
|
}
|
|
d = d[2:]
|
|
l = int(d[0])
|
|
if l != len(d)-1 {
|
|
return false
|
|
}
|
|
d = d[1:]
|
|
m.alpnProtocol = string(d)
|
|
m.alpnProtocolEmpty = len(d) == 0
|
|
case extensionChannelID:
|
|
if length > 0 {
|
|
return false
|
|
}
|
|
m.channelIDRequested = true
|
|
case extensionExtendedMasterSecret:
|
|
if length != 0 {
|
|
return false
|
|
}
|
|
m.extendedMasterSecret = true
|
|
case extensionUseSRTP:
|
|
if length < 2+2+1 {
|
|
return false
|
|
}
|
|
if data[0] != 0 || data[1] != 2 {
|
|
return false
|
|
}
|
|
m.srtpProtectionProfile = uint16(data[2])<<8 | uint16(data[3])
|
|
d := data[4:length]
|
|
l := int(d[0])
|
|
if l != len(d)-1 {
|
|
return false
|
|
}
|
|
m.srtpMasterKeyIdentifier = string(d[1:])
|
|
case extensionSignedCertificateTimestamp:
|
|
m.sctList = data[:length]
|
|
case extensionCustom:
|
|
m.customExtension = string(data[:length])
|
|
case extensionServerName:
|
|
if length != 0 {
|
|
return false
|
|
}
|
|
// Ignore this extension from the server.
|
|
case extensionSupportedPoints:
|
|
// supported_points is illegal in TLS 1.3.
|
|
if version >= VersionTLS13 {
|
|
return false
|
|
}
|
|
// http://tools.ietf.org/html/rfc4492#section-5.5.2
|
|
if length < 1 {
|
|
return false
|
|
}
|
|
l := int(data[0])
|
|
if length != l+1 {
|
|
return false
|
|
}
|
|
m.supportedPoints = data[1 : 1+l]
|
|
case extensionSupportedCurves:
|
|
// The server can only send supported_curves in TLS 1.3.
|
|
if version < VersionTLS13 {
|
|
return false
|
|
}
|
|
default:
|
|
// Unknown extensions are illegal from the server.
|
|
return false
|
|
}
|
|
data = data[length:]
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type helloRetryRequestMsg struct {
|
|
raw []byte
|
|
vers uint16
|
|
hasSelectedGroup bool
|
|
selectedGroup CurveID
|
|
cookie []byte
|
|
customExtension string
|
|
duplicateExtensions bool
|
|
}
|
|
|
|
func (m *helloRetryRequestMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
retryRequestMsg := newByteBuilder()
|
|
retryRequestMsg.addU8(typeHelloRetryRequest)
|
|
retryRequest := retryRequestMsg.addU24LengthPrefixed()
|
|
retryRequest.addU16(m.vers)
|
|
extensions := retryRequest.addU16LengthPrefixed()
|
|
|
|
count := 1
|
|
if m.duplicateExtensions {
|
|
count = 2
|
|
}
|
|
|
|
for i := 0; i < count; i++ {
|
|
if m.hasSelectedGroup {
|
|
extensions.addU16(extensionKeyShare)
|
|
extensions.addU16(2) // length
|
|
extensions.addU16(uint16(m.selectedGroup))
|
|
}
|
|
if len(m.cookie) > 0 {
|
|
extensions.addU16(extensionCookie)
|
|
body := extensions.addU16LengthPrefixed()
|
|
body.addU16LengthPrefixed().addBytes(m.cookie)
|
|
}
|
|
if len(m.customExtension) > 0 {
|
|
extensions.addU16(extensionCustom)
|
|
extensions.addU16LengthPrefixed().addBytes([]byte(m.customExtension))
|
|
}
|
|
}
|
|
|
|
m.raw = retryRequestMsg.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *helloRetryRequestMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
if len(data) < 8 {
|
|
return false
|
|
}
|
|
m.vers = uint16(data[4])<<8 | uint16(data[5])
|
|
extLen := int(data[6])<<8 | int(data[7])
|
|
data = data[8:]
|
|
if len(data) != extLen || len(data) == 0 {
|
|
return false
|
|
}
|
|
for len(data) > 0 {
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
extension := uint16(data[0])<<8 | uint16(data[1])
|
|
length := int(data[2])<<8 | int(data[3])
|
|
data = data[4:]
|
|
if len(data) < length {
|
|
return false
|
|
}
|
|
|
|
switch extension {
|
|
case extensionKeyShare:
|
|
if length != 2 {
|
|
return false
|
|
}
|
|
m.hasSelectedGroup = true
|
|
m.selectedGroup = CurveID(data[0])<<8 | CurveID(data[1])
|
|
case extensionCookie:
|
|
if length < 2 {
|
|
return false
|
|
}
|
|
cookieLen := int(data[0])<<8 | int(data[1])
|
|
if 2+cookieLen != length {
|
|
return false
|
|
}
|
|
m.cookie = data[2 : 2+cookieLen]
|
|
default:
|
|
// Unknown extensions are illegal from the server.
|
|
return false
|
|
}
|
|
data = data[length:]
|
|
}
|
|
return true
|
|
}
|
|
|
|
type certificateEntry struct {
|
|
data []byte
|
|
ocspResponse []byte
|
|
sctList []byte
|
|
duplicateExtensions bool
|
|
extraExtension []byte
|
|
}
|
|
|
|
type certificateMsg struct {
|
|
raw []byte
|
|
hasRequestContext bool
|
|
requestContext []byte
|
|
certificates []certificateEntry
|
|
}
|
|
|
|
func (m *certificateMsg) marshal() (x []byte) {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
certMsg := newByteBuilder()
|
|
certMsg.addU8(typeCertificate)
|
|
certificate := certMsg.addU24LengthPrefixed()
|
|
if m.hasRequestContext {
|
|
context := certificate.addU8LengthPrefixed()
|
|
context.addBytes(m.requestContext)
|
|
}
|
|
certificateList := certificate.addU24LengthPrefixed()
|
|
for _, cert := range m.certificates {
|
|
certEntry := certificateList.addU24LengthPrefixed()
|
|
certEntry.addBytes(cert.data)
|
|
if m.hasRequestContext {
|
|
extensions := certificateList.addU16LengthPrefixed()
|
|
count := 1
|
|
if cert.duplicateExtensions {
|
|
count = 2
|
|
}
|
|
|
|
for i := 0; i < count; i++ {
|
|
if cert.ocspResponse != nil {
|
|
extensions.addU16(extensionStatusRequest)
|
|
body := extensions.addU16LengthPrefixed()
|
|
body.addU8(statusTypeOCSP)
|
|
response := body.addU24LengthPrefixed()
|
|
response.addBytes(cert.ocspResponse)
|
|
}
|
|
|
|
if cert.sctList != nil {
|
|
extensions.addU16(extensionSignedCertificateTimestamp)
|
|
extension := extensions.addU16LengthPrefixed()
|
|
extension.addBytes(cert.sctList)
|
|
}
|
|
}
|
|
if cert.extraExtension != nil {
|
|
extensions.addBytes(cert.extraExtension)
|
|
}
|
|
}
|
|
}
|
|
|
|
m.raw = certMsg.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *certificateMsg) unmarshal(data []byte) bool {
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
|
|
m.raw = data
|
|
data = data[4:]
|
|
|
|
if m.hasRequestContext {
|
|
if len(data) == 0 {
|
|
return false
|
|
}
|
|
contextLen := int(data[0])
|
|
if len(data) < 1+contextLen {
|
|
return false
|
|
}
|
|
m.requestContext = make([]byte, contextLen)
|
|
copy(m.requestContext, data[1:])
|
|
data = data[1+contextLen:]
|
|
}
|
|
|
|
if len(data) < 3 {
|
|
return false
|
|
}
|
|
certsLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
|
|
data = data[3:]
|
|
if len(data) != certsLen {
|
|
return false
|
|
}
|
|
|
|
m.certificates = nil
|
|
for len(data) != 0 {
|
|
if len(data) < 3 {
|
|
return false
|
|
}
|
|
certLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
|
|
if len(data) < 3+certLen {
|
|
return false
|
|
}
|
|
cert := certificateEntry{
|
|
data: data[3 : 3+certLen],
|
|
}
|
|
data = data[3+certLen:]
|
|
if m.hasRequestContext {
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
extensionsLen := int(data[0])<<8 | int(data[1])
|
|
if len(data) < 2+extensionsLen {
|
|
return false
|
|
}
|
|
extensions := data[2 : 2+extensionsLen]
|
|
data = data[2+extensionsLen:]
|
|
for len(extensions) != 0 {
|
|
if len(extensions) < 4 {
|
|
return false
|
|
}
|
|
extension := uint16(extensions[0])<<8 | uint16(extensions[1])
|
|
length := int(extensions[2])<<8 | int(extensions[3])
|
|
if len(extensions) < 4+length {
|
|
return false
|
|
}
|
|
contents := extensions[4 : 4+length]
|
|
extensions = extensions[4+length:]
|
|
|
|
switch extension {
|
|
case extensionStatusRequest:
|
|
if length < 4 {
|
|
return false
|
|
}
|
|
if contents[0] != statusTypeOCSP {
|
|
return false
|
|
}
|
|
respLen := int(contents[1])<<16 | int(contents[2])<<8 | int(contents[3])
|
|
if respLen+4 != len(contents) || respLen == 0 {
|
|
return false
|
|
}
|
|
cert.ocspResponse = contents[4:]
|
|
case extensionSignedCertificateTimestamp:
|
|
cert.sctList = contents
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
m.certificates = append(m.certificates, cert)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type serverKeyExchangeMsg struct {
|
|
raw []byte
|
|
key []byte
|
|
}
|
|
|
|
func (m *serverKeyExchangeMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
length := len(m.key)
|
|
x := make([]byte, length+4)
|
|
x[0] = typeServerKeyExchange
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
copy(x[4:], m.key)
|
|
|
|
m.raw = x
|
|
return x
|
|
}
|
|
|
|
func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
m.key = data[4:]
|
|
return true
|
|
}
|
|
|
|
type certificateStatusMsg struct {
|
|
raw []byte
|
|
statusType uint8
|
|
response []byte
|
|
}
|
|
|
|
func (m *certificateStatusMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
var x []byte
|
|
if m.statusType == statusTypeOCSP {
|
|
x = make([]byte, 4+4+len(m.response))
|
|
x[0] = typeCertificateStatus
|
|
l := len(m.response) + 4
|
|
x[1] = byte(l >> 16)
|
|
x[2] = byte(l >> 8)
|
|
x[3] = byte(l)
|
|
x[4] = statusTypeOCSP
|
|
|
|
l -= 4
|
|
x[5] = byte(l >> 16)
|
|
x[6] = byte(l >> 8)
|
|
x[7] = byte(l)
|
|
copy(x[8:], m.response)
|
|
} else {
|
|
x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType}
|
|
}
|
|
|
|
m.raw = x
|
|
return x
|
|
}
|
|
|
|
func (m *certificateStatusMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
if len(data) < 5 {
|
|
return false
|
|
}
|
|
m.statusType = data[4]
|
|
|
|
m.response = nil
|
|
if m.statusType == statusTypeOCSP {
|
|
if len(data) < 8 {
|
|
return false
|
|
}
|
|
respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
|
|
if uint32(len(data)) != 4+4+respLen {
|
|
return false
|
|
}
|
|
m.response = data[8:]
|
|
}
|
|
return true
|
|
}
|
|
|
|
type serverHelloDoneMsg struct{}
|
|
|
|
func (m *serverHelloDoneMsg) marshal() []byte {
|
|
x := make([]byte, 4)
|
|
x[0] = typeServerHelloDone
|
|
return x
|
|
}
|
|
|
|
func (m *serverHelloDoneMsg) unmarshal(data []byte) bool {
|
|
return len(data) == 4
|
|
}
|
|
|
|
type clientKeyExchangeMsg struct {
|
|
raw []byte
|
|
ciphertext []byte
|
|
}
|
|
|
|
func (m *clientKeyExchangeMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
length := len(m.ciphertext)
|
|
x := make([]byte, length+4)
|
|
x[0] = typeClientKeyExchange
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
copy(x[4:], m.ciphertext)
|
|
|
|
m.raw = x
|
|
return x
|
|
}
|
|
|
|
func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
l := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
|
if l != len(data)-4 {
|
|
return false
|
|
}
|
|
m.ciphertext = data[4:]
|
|
return true
|
|
}
|
|
|
|
type finishedMsg struct {
|
|
raw []byte
|
|
verifyData []byte
|
|
}
|
|
|
|
func (m *finishedMsg) marshal() (x []byte) {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
x = make([]byte, 4+len(m.verifyData))
|
|
x[0] = typeFinished
|
|
x[3] = byte(len(m.verifyData))
|
|
copy(x[4:], m.verifyData)
|
|
m.raw = x
|
|
return
|
|
}
|
|
|
|
func (m *finishedMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
m.verifyData = data[4:]
|
|
return true
|
|
}
|
|
|
|
type nextProtoMsg struct {
|
|
raw []byte
|
|
proto string
|
|
}
|
|
|
|
func (m *nextProtoMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
l := len(m.proto)
|
|
if l > 255 {
|
|
l = 255
|
|
}
|
|
|
|
padding := 32 - (l+2)%32
|
|
length := l + padding + 2
|
|
x := make([]byte, length+4)
|
|
x[0] = typeNextProtocol
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
|
|
y := x[4:]
|
|
y[0] = byte(l)
|
|
copy(y[1:], []byte(m.proto[0:l]))
|
|
y = y[1+l:]
|
|
y[0] = byte(padding)
|
|
|
|
m.raw = x
|
|
|
|
return x
|
|
}
|
|
|
|
func (m *nextProtoMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
|
|
if len(data) < 5 {
|
|
return false
|
|
}
|
|
data = data[4:]
|
|
protoLen := int(data[0])
|
|
data = data[1:]
|
|
if len(data) < protoLen {
|
|
return false
|
|
}
|
|
m.proto = string(data[0:protoLen])
|
|
data = data[protoLen:]
|
|
|
|
if len(data) < 1 {
|
|
return false
|
|
}
|
|
paddingLen := int(data[0])
|
|
data = data[1:]
|
|
if len(data) != paddingLen {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type certificateRequestMsg struct {
|
|
raw []byte
|
|
// hasSignatureAlgorithm indicates whether this message includes a list
|
|
// of signature and hash functions. This change was introduced with TLS
|
|
// 1.2.
|
|
hasSignatureAlgorithm bool
|
|
// hasRequestContext indicates whether this message includes a context
|
|
// field instead of certificateTypes. This change was introduced with
|
|
// TLS 1.3.
|
|
hasRequestContext bool
|
|
|
|
certificateTypes []byte
|
|
requestContext []byte
|
|
signatureAlgorithms []signatureAlgorithm
|
|
certificateAuthorities [][]byte
|
|
}
|
|
|
|
func (m *certificateRequestMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
// See http://tools.ietf.org/html/rfc4346#section-7.4.4
|
|
builder := newByteBuilder()
|
|
builder.addU8(typeCertificateRequest)
|
|
body := builder.addU24LengthPrefixed()
|
|
|
|
if m.hasRequestContext {
|
|
requestContext := body.addU8LengthPrefixed()
|
|
requestContext.addBytes(m.requestContext)
|
|
} else {
|
|
certificateTypes := body.addU8LengthPrefixed()
|
|
certificateTypes.addBytes(m.certificateTypes)
|
|
}
|
|
|
|
if m.hasSignatureAlgorithm {
|
|
signatureAlgorithms := body.addU16LengthPrefixed()
|
|
for _, sigAlg := range m.signatureAlgorithms {
|
|
signatureAlgorithms.addU16(uint16(sigAlg))
|
|
}
|
|
}
|
|
|
|
certificateAuthorities := body.addU16LengthPrefixed()
|
|
for _, ca := range m.certificateAuthorities {
|
|
caEntry := certificateAuthorities.addU16LengthPrefixed()
|
|
caEntry.addBytes(ca)
|
|
}
|
|
|
|
if m.hasRequestContext {
|
|
// Emit no certificate extensions.
|
|
body.addU16(0)
|
|
}
|
|
|
|
m.raw = builder.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *certificateRequestMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
|
|
if len(data) < 5 {
|
|
return false
|
|
}
|
|
data = data[4:]
|
|
|
|
if m.hasRequestContext {
|
|
contextLen := int(data[0])
|
|
if len(data) < 1+contextLen {
|
|
return false
|
|
}
|
|
m.requestContext = make([]byte, contextLen)
|
|
copy(m.requestContext, data[1:])
|
|
data = data[1+contextLen:]
|
|
} else {
|
|
numCertTypes := int(data[0])
|
|
if len(data) < 1+numCertTypes {
|
|
return false
|
|
}
|
|
m.certificateTypes = make([]byte, numCertTypes)
|
|
copy(m.certificateTypes, data[1:])
|
|
data = data[1+numCertTypes:]
|
|
}
|
|
|
|
if m.hasSignatureAlgorithm {
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
sigAlgsLen := uint16(data[0])<<8 | uint16(data[1])
|
|
data = data[2:]
|
|
if sigAlgsLen&1 != 0 {
|
|
return false
|
|
}
|
|
if len(data) < int(sigAlgsLen) {
|
|
return false
|
|
}
|
|
numSigAlgs := sigAlgsLen / 2
|
|
m.signatureAlgorithms = make([]signatureAlgorithm, numSigAlgs)
|
|
for i := range m.signatureAlgorithms {
|
|
m.signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1])
|
|
data = data[2:]
|
|
}
|
|
}
|
|
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
casLength := uint16(data[0])<<8 | uint16(data[1])
|
|
data = data[2:]
|
|
if len(data) < int(casLength) {
|
|
return false
|
|
}
|
|
cas := make([]byte, casLength)
|
|
copy(cas, data)
|
|
data = data[casLength:]
|
|
|
|
m.certificateAuthorities = nil
|
|
for len(cas) > 0 {
|
|
if len(cas) < 2 {
|
|
return false
|
|
}
|
|
caLen := uint16(cas[0])<<8 | uint16(cas[1])
|
|
cas = cas[2:]
|
|
|
|
if len(cas) < int(caLen) {
|
|
return false
|
|
}
|
|
|
|
m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen])
|
|
cas = cas[caLen:]
|
|
}
|
|
|
|
if m.hasRequestContext {
|
|
// Ignore certificate extensions.
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
extsLength := int(data[0])<<8 | int(data[1])
|
|
if len(data) < 2+extsLength {
|
|
return false
|
|
}
|
|
data = data[2+extsLength:]
|
|
}
|
|
|
|
if len(data) > 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type certificateVerifyMsg struct {
|
|
raw []byte
|
|
hasSignatureAlgorithm bool
|
|
signatureAlgorithm signatureAlgorithm
|
|
signature []byte
|
|
}
|
|
|
|
func (m *certificateVerifyMsg) marshal() (x []byte) {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
// See http://tools.ietf.org/html/rfc4346#section-7.4.8
|
|
siglength := len(m.signature)
|
|
length := 2 + siglength
|
|
if m.hasSignatureAlgorithm {
|
|
length += 2
|
|
}
|
|
x = make([]byte, 4+length)
|
|
x[0] = typeCertificateVerify
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
y := x[4:]
|
|
if m.hasSignatureAlgorithm {
|
|
y[0] = byte(m.signatureAlgorithm >> 8)
|
|
y[1] = byte(m.signatureAlgorithm)
|
|
y = y[2:]
|
|
}
|
|
y[0] = uint8(siglength >> 8)
|
|
y[1] = uint8(siglength)
|
|
copy(y[2:], m.signature)
|
|
|
|
m.raw = x
|
|
|
|
return
|
|
}
|
|
|
|
func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
|
|
if len(data) < 6 {
|
|
return false
|
|
}
|
|
|
|
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
|
if uint32(len(data))-4 != length {
|
|
return false
|
|
}
|
|
|
|
data = data[4:]
|
|
if m.hasSignatureAlgorithm {
|
|
m.signatureAlgorithm = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1])
|
|
data = data[2:]
|
|
}
|
|
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
siglength := int(data[0])<<8 + int(data[1])
|
|
data = data[2:]
|
|
if len(data) != siglength {
|
|
return false
|
|
}
|
|
|
|
m.signature = data
|
|
|
|
return true
|
|
}
|
|
|
|
type newSessionTicketMsg struct {
|
|
raw []byte
|
|
version uint16
|
|
ticketLifetime uint32
|
|
ticketAgeAdd uint32
|
|
ticket []byte
|
|
earlyDataInfo uint32
|
|
customExtension string
|
|
duplicateEarlyDataInfo bool
|
|
hasGREASEExtension bool
|
|
}
|
|
|
|
func (m *newSessionTicketMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
// See http://tools.ietf.org/html/rfc5077#section-3.3
|
|
ticketMsg := newByteBuilder()
|
|
ticketMsg.addU8(typeNewSessionTicket)
|
|
body := ticketMsg.addU24LengthPrefixed()
|
|
body.addU32(m.ticketLifetime)
|
|
if m.version >= VersionTLS13 {
|
|
body.addU32(m.ticketAgeAdd)
|
|
}
|
|
|
|
ticket := body.addU16LengthPrefixed()
|
|
ticket.addBytes(m.ticket)
|
|
|
|
if m.version >= VersionTLS13 {
|
|
extensions := body.addU16LengthPrefixed()
|
|
if m.earlyDataInfo > 0 {
|
|
extensions.addU16(extensionTicketEarlyDataInfo)
|
|
extensions.addU16LengthPrefixed().addU32(m.earlyDataInfo)
|
|
if m.duplicateEarlyDataInfo {
|
|
extensions.addU16(extensionTicketEarlyDataInfo)
|
|
extensions.addU16LengthPrefixed().addU32(m.earlyDataInfo)
|
|
}
|
|
}
|
|
if len(m.customExtension) > 0 {
|
|
extensions.addU16(extensionCustom)
|
|
extensions.addU16LengthPrefixed().addBytes([]byte(m.customExtension))
|
|
}
|
|
}
|
|
|
|
m.raw = ticketMsg.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
|
|
if len(data) < 8 {
|
|
return false
|
|
}
|
|
m.ticketLifetime = uint32(data[4])<<24 | uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
|
|
data = data[8:]
|
|
|
|
if m.version >= VersionTLS13 {
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
m.ticketAgeAdd = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
|
data = data[4:]
|
|
}
|
|
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
ticketLen := int(data[0])<<8 + int(data[1])
|
|
data = data[2:]
|
|
if len(data) < ticketLen {
|
|
return false
|
|
}
|
|
|
|
if m.version >= VersionTLS13 && ticketLen == 0 {
|
|
return false
|
|
}
|
|
|
|
m.ticket = data[:ticketLen]
|
|
data = data[ticketLen:]
|
|
|
|
if m.version >= VersionTLS13 {
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
|
|
extensionsLength := int(data[0])<<8 | int(data[1])
|
|
data = data[2:]
|
|
if extensionsLength != len(data) {
|
|
return false
|
|
}
|
|
|
|
for len(data) != 0 {
|
|
if len(data) < 4 {
|
|
return false
|
|
}
|
|
extension := uint16(data[0])<<8 | uint16(data[1])
|
|
length := int(data[2])<<8 | int(data[3])
|
|
data = data[4:]
|
|
if len(data) < length {
|
|
return false
|
|
}
|
|
|
|
switch extension {
|
|
case extensionTicketEarlyDataInfo:
|
|
if length != 4 {
|
|
return false
|
|
}
|
|
m.earlyDataInfo = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
|
default:
|
|
if isGREASEValue(extension) {
|
|
m.hasGREASEExtension = true
|
|
}
|
|
}
|
|
|
|
data = data[length:]
|
|
}
|
|
}
|
|
|
|
if len(data) > 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type v2ClientHelloMsg struct {
|
|
raw []byte
|
|
vers uint16
|
|
cipherSuites []uint16
|
|
sessionId []byte
|
|
challenge []byte
|
|
}
|
|
|
|
func (m *v2ClientHelloMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
length := 1 + 2 + 2 + 2 + 2 + len(m.cipherSuites)*3 + len(m.sessionId) + len(m.challenge)
|
|
|
|
x := make([]byte, length)
|
|
x[0] = 1
|
|
x[1] = uint8(m.vers >> 8)
|
|
x[2] = uint8(m.vers)
|
|
x[3] = uint8((len(m.cipherSuites) * 3) >> 8)
|
|
x[4] = uint8(len(m.cipherSuites) * 3)
|
|
x[5] = uint8(len(m.sessionId) >> 8)
|
|
x[6] = uint8(len(m.sessionId))
|
|
x[7] = uint8(len(m.challenge) >> 8)
|
|
x[8] = uint8(len(m.challenge))
|
|
y := x[9:]
|
|
for i, spec := range m.cipherSuites {
|
|
y[i*3] = 0
|
|
y[i*3+1] = uint8(spec >> 8)
|
|
y[i*3+2] = uint8(spec)
|
|
}
|
|
y = y[len(m.cipherSuites)*3:]
|
|
copy(y, m.sessionId)
|
|
y = y[len(m.sessionId):]
|
|
copy(y, m.challenge)
|
|
|
|
m.raw = x
|
|
|
|
return x
|
|
}
|
|
|
|
type helloVerifyRequestMsg struct {
|
|
raw []byte
|
|
vers uint16
|
|
cookie []byte
|
|
}
|
|
|
|
func (m *helloVerifyRequestMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
length := 2 + 1 + len(m.cookie)
|
|
|
|
x := make([]byte, 4+length)
|
|
x[0] = typeHelloVerifyRequest
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
vers := m.vers
|
|
x[4] = uint8(vers >> 8)
|
|
x[5] = uint8(vers)
|
|
x[6] = uint8(len(m.cookie))
|
|
copy(x[7:7+len(m.cookie)], m.cookie)
|
|
|
|
return x
|
|
}
|
|
|
|
func (m *helloVerifyRequestMsg) unmarshal(data []byte) bool {
|
|
if len(data) < 4+2+1 {
|
|
return false
|
|
}
|
|
m.raw = data
|
|
m.vers = uint16(data[4])<<8 | uint16(data[5])
|
|
cookieLen := int(data[6])
|
|
if cookieLen > 32 || len(data) != 7+cookieLen {
|
|
return false
|
|
}
|
|
m.cookie = data[7 : 7+cookieLen]
|
|
|
|
return true
|
|
}
|
|
|
|
type channelIDMsg struct {
|
|
raw []byte
|
|
channelID []byte
|
|
}
|
|
|
|
func (m *channelIDMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
length := 2 + 2 + len(m.channelID)
|
|
|
|
x := make([]byte, 4+length)
|
|
x[0] = typeChannelID
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
x[4] = uint8(extensionChannelID >> 8)
|
|
x[5] = uint8(extensionChannelID & 0xff)
|
|
x[6] = uint8(len(m.channelID) >> 8)
|
|
x[7] = uint8(len(m.channelID) & 0xff)
|
|
copy(x[8:], m.channelID)
|
|
|
|
return x
|
|
}
|
|
|
|
func (m *channelIDMsg) unmarshal(data []byte) bool {
|
|
if len(data) != 4+2+2+128 {
|
|
return false
|
|
}
|
|
m.raw = data
|
|
if (uint16(data[4])<<8)|uint16(data[5]) != extensionChannelID {
|
|
return false
|
|
}
|
|
if int(data[6])<<8|int(data[7]) != 128 {
|
|
return false
|
|
}
|
|
m.channelID = data[4+2+2:]
|
|
|
|
return true
|
|
}
|
|
|
|
type helloRequestMsg struct {
|
|
}
|
|
|
|
func (*helloRequestMsg) marshal() []byte {
|
|
return []byte{typeHelloRequest, 0, 0, 0}
|
|
}
|
|
|
|
func (*helloRequestMsg) unmarshal(data []byte) bool {
|
|
return len(data) == 4
|
|
}
|
|
|
|
type keyUpdateMsg struct {
|
|
raw []byte
|
|
keyUpdateRequest byte
|
|
}
|
|
|
|
func (m *keyUpdateMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
return []byte{typeKeyUpdate, 0, 0, 1, m.keyUpdateRequest}
|
|
}
|
|
|
|
func (m *keyUpdateMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
|
|
if len(data) != 5 {
|
|
return false
|
|
}
|
|
|
|
length := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
|
if len(data)-4 != length {
|
|
return false
|
|
}
|
|
|
|
m.keyUpdateRequest = data[4]
|
|
return m.keyUpdateRequest == keyUpdateNotRequested || m.keyUpdateRequest == keyUpdateRequested
|
|
}
|
|
|
|
// ssl3NoCertificateMsg is a dummy message to handle SSL 3.0 using a warning
|
|
// alert in the handshake.
|
|
type ssl3NoCertificateMsg struct{}
|
|
|
|
func eqUint16s(x, y []uint16) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
if y[i] != v {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func eqCurveIDs(x, y []CurveID) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
if y[i] != v {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func eqStrings(x, y []string) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
if y[i] != v {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func eqByteSlices(x, y [][]byte) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
if !bytes.Equal(v, y[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func eqSignatureAlgorithms(x, y []signatureAlgorithm) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
v2 := y[i]
|
|
if v != v2 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func eqKeyShareEntryLists(x, y []keyShareEntry) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
if y[i].group != v.group || !bytes.Equal(y[i].keyExchange, v.keyExchange) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
}
|
|
|
|
func eqPSKIdentityLists(x, y []pskIdentity) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
if !bytes.Equal(y[i].ticket, v.ticket) || y[i].obfuscatedTicketAge != v.obfuscatedTicketAge {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
}
|