65ac997f20
This GREASEs cipher suites, groups, and extensions. For now, we'll always place them in a hard-coded position. We can experiment with more interesting strategies later. If we add new ciphers and curves, presumably we prefer them over current ones, so place GREASE values at the front. This prevents implementations from parsing only the first value and ignoring the rest. Add two new extensions, one empty and one non-empty. Place the empty one in front (IBM WebSphere can't handle trailing empty extensions) and the non-empty one at the end. Change-Id: If2e009936bc298cedf2a7a593ce7d5d5ddbb841a Reviewed-on: https://boringssl-review.googlesource.com/11241 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: Adam Langley <agl@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2037 lines
46 KiB
Go
2037 lines
46 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 clientHelloMsg struct {
|
|
raw []byte
|
|
isDTLS bool
|
|
vers uint16
|
|
random []byte
|
|
sessionId []byte
|
|
// TODO(davidben): Add support for TLS 1.3 cookies which are larger and
|
|
// use an extension.
|
|
cookie []byte
|
|
cipherSuites []uint16
|
|
compressionMethods []uint8
|
|
nextProtoNeg bool
|
|
serverName string
|
|
ocspStapling bool
|
|
supportedCurves []CurveID
|
|
supportedPoints []uint8
|
|
hasKeyShares bool
|
|
keyShares []keyShareEntry
|
|
trailingKeyShareData bool
|
|
pskIdentities [][]uint8
|
|
hasEarlyData bool
|
|
earlyDataContext []byte
|
|
ticketSupported bool
|
|
sessionTicket []uint8
|
|
signatureAlgorithms []signatureAlgorithm
|
|
secureRenegotiation []byte
|
|
alpnProtocols []string
|
|
duplicateExtension bool
|
|
channelIDSupported bool
|
|
npnLast bool
|
|
extendedMasterSecret bool
|
|
srtpProtectionProfiles []uint16
|
|
srtpMasterKeyIdentifier string
|
|
sctListSupported bool
|
|
customExtension string
|
|
hasGREASEExtension 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 &&
|
|
eqByteSlices(m.pskIdentities, m1.pskIdentities) &&
|
|
m.hasEarlyData == m1.hasEarlyData &&
|
|
bytes.Equal(m.earlyDataContext, m1.earlyDataContext) &&
|
|
m.ticketSupported == m1.ticketSupported &&
|
|
bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
|
|
eqSignatureAlgorithms(m.signatureAlgorithms, m1.signatureAlgorithms) &&
|
|
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.npnLast == m1.npnLast &&
|
|
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
|
|
}
|
|
|
|
func (m *clientHelloMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
handshakeMsg := newByteBuilder()
|
|
handshakeMsg.addU8(typeClientHello)
|
|
hello := handshakeMsg.addU24LengthPrefixed()
|
|
vers := versionToWire(m.vers, m.isDTLS)
|
|
hello.addU16(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 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.npnLast {
|
|
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()
|
|
for _, pointFormat := range m.supportedPoints {
|
|
supportedPoints.addU8(pointFormat)
|
|
}
|
|
}
|
|
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.pskIdentities) > 0 {
|
|
extensions.addU16(extensionPreSharedKey)
|
|
pskExtension := extensions.addU16LengthPrefixed()
|
|
|
|
pskIdentities := pskExtension.addU16LengthPrefixed()
|
|
for _, psk := range m.pskIdentities {
|
|
pskIdentity := pskIdentities.addU16LengthPrefixed()
|
|
pskIdentity.addBytes(psk)
|
|
}
|
|
}
|
|
if m.hasEarlyData {
|
|
extensions.addU16(extensionEarlyData)
|
|
earlyDataIndication := extensions.addU16LengthPrefixed()
|
|
|
|
context := earlyDataIndication.addU8LengthPrefixed()
|
|
context.addBytes(m.earlyDataContext)
|
|
}
|
|
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 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.npnLast {
|
|
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.vers == VersionTLS13 {
|
|
extensions.addU16(extensionTLS13Draft)
|
|
extValue := extensions.addU16LengthPrefixed()
|
|
extValue.addU16(tls13DraftVersion)
|
|
}
|
|
|
|
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 = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS)
|
|
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.earlyDataContext = nil
|
|
m.ticketSupported = false
|
|
m.sessionTicket = nil
|
|
m.signatureAlgorithms = 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 = make([]uint8, l)
|
|
copy(m.supportedPoints, data[1:])
|
|
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 section 6.3.2.4
|
|
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 {
|
|
if len(d) < 2 {
|
|
return false
|
|
}
|
|
pskLen := int(d[0])<<8 | int(d[1])
|
|
d = d[2:]
|
|
if len(d) < pskLen {
|
|
return false
|
|
}
|
|
psk := d[:pskLen]
|
|
m.pskIdentities = append(m.pskIdentities, psk)
|
|
d = d[pskLen:]
|
|
}
|
|
case extensionEarlyData:
|
|
// draft-ietf-tls-tls13 section 6.3.2.5
|
|
if length < 1 {
|
|
return false
|
|
}
|
|
l := int(data[0])
|
|
if length != l+1 {
|
|
return false
|
|
}
|
|
m.hasEarlyData = true
|
|
m.earlyDataContext = data[1:length]
|
|
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 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 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
|
|
random []byte
|
|
sessionId []byte
|
|
cipherSuite uint16
|
|
hasKeyShare bool
|
|
keyShare keyShareEntry
|
|
hasPSKIdentity bool
|
|
pskIdentity uint16
|
|
earlyDataIndication bool
|
|
compressionMethod uint8
|
|
extensions serverExtensions
|
|
}
|
|
|
|
func (m *serverHelloMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
handshakeMsg := newByteBuilder()
|
|
handshakeMsg.addU8(typeServerHello)
|
|
hello := handshakeMsg.addU24LengthPrefixed()
|
|
vers := versionToWire(m.vers, m.isDTLS)
|
|
hello.addU16(vers)
|
|
hello.addBytes(m.random)
|
|
if m.vers < VersionTLS13 {
|
|
sessionId := hello.addU8LengthPrefixed()
|
|
sessionId.addBytes(m.sessionId)
|
|
}
|
|
hello.addU16(m.cipherSuite)
|
|
if m.vers < VersionTLS13 {
|
|
hello.addU8(m.compressionMethod)
|
|
}
|
|
|
|
extensions := hello.addU16LengthPrefixed()
|
|
|
|
if m.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
|
|
}
|
|
} else {
|
|
m.extensions.marshal(extensions, m.vers)
|
|
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 = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS)
|
|
m.random = data[6:38]
|
|
data = data[38:]
|
|
if m.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 m.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 m.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
|
|
default:
|
|
// Only allow the 3 extensions that are sent in
|
|
// the clear in TLS 1.3.
|
|
return false
|
|
}
|
|
}
|
|
} else if !m.extensions.unmarshal(data, m.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, VersionTLS13)
|
|
}
|
|
|
|
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
|
|
ocspResponse []byte
|
|
ticketSupported bool
|
|
secureRenegotiation []byte
|
|
alpnProtocol string
|
|
alpnProtocolEmpty bool
|
|
duplicateExtension bool
|
|
channelIDRequested bool
|
|
extendedMasterSecret bool
|
|
srtpProtectionProfile uint16
|
|
srtpMasterKeyIdentifier string
|
|
sctList []byte
|
|
customExtension string
|
|
npnLast bool
|
|
hasKeyShare bool
|
|
keyShare keyShareEntry
|
|
}
|
|
|
|
func (m *serverExtensions) marshal(extensions *byteBuilder, version uint16) {
|
|
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.npnLast {
|
|
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 version >= VersionTLS13 {
|
|
if m.ocspResponse != nil {
|
|
extensions.addU16(extensionStatusRequest)
|
|
body := extensions.addU16LengthPrefixed()
|
|
body.addU8(statusTypeOCSP)
|
|
response := body.addU24LengthPrefixed()
|
|
response.addBytes(m.ocspResponse)
|
|
}
|
|
} else {
|
|
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.npnLast {
|
|
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)
|
|
}
|
|
}
|
|
|
|
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 version >= VersionTLS13 {
|
|
if length < 4 {
|
|
return false
|
|
}
|
|
d := data[:length]
|
|
if d[0] != statusTypeOCSP {
|
|
return false
|
|
}
|
|
respLen := int(d[1])<<16 | int(d[2])<<8 | int(d[3])
|
|
if respLen+4 != len(d) || respLen == 0 {
|
|
return false
|
|
}
|
|
m.ocspResponse = d[4:]
|
|
} else {
|
|
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
|
|
}
|
|
// Ignore this extension from the server.
|
|
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
|
|
cipherSuite uint16
|
|
selectedGroup CurveID
|
|
}
|
|
|
|
func (m *helloRetryRequestMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
retryRequestMsg := newByteBuilder()
|
|
retryRequestMsg.addU8(typeHelloRetryRequest)
|
|
retryRequest := retryRequestMsg.addU24LengthPrefixed()
|
|
retryRequest.addU16(m.vers)
|
|
retryRequest.addU16(m.cipherSuite)
|
|
retryRequest.addU16(uint16(m.selectedGroup))
|
|
// Extensions field. We have none to send.
|
|
retryRequest.addU16(0)
|
|
|
|
m.raw = retryRequestMsg.finish()
|
|
return m.raw
|
|
}
|
|
|
|
func (m *helloRetryRequestMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
if len(data) < 12 {
|
|
return false
|
|
}
|
|
m.vers = uint16(data[4])<<8 | uint16(data[5])
|
|
m.cipherSuite = uint16(data[6])<<8 | uint16(data[7])
|
|
m.selectedGroup = CurveID(data[8])<<8 | CurveID(data[9])
|
|
extLen := int(data[10])<<8 | int(data[11])
|
|
data = data[12:]
|
|
if len(data) != extLen {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type certificateMsg struct {
|
|
raw []byte
|
|
hasRequestContext bool
|
|
requestContext []byte
|
|
certificates [][]byte
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
numCerts := 0
|
|
d := data
|
|
for certsLen > 0 {
|
|
if len(d) < 4 {
|
|
return false
|
|
}
|
|
certLen := int(d[0])<<16 | int(d[1])<<8 | int(d[2])
|
|
if len(d) < 3+certLen {
|
|
return false
|
|
}
|
|
d = d[3+certLen:]
|
|
certsLen -= 3 + certLen
|
|
numCerts++
|
|
}
|
|
|
|
m.certificates = make([][]byte, numCerts)
|
|
d = data
|
|
for i := 0; i < numCerts; i++ {
|
|
certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
|
|
m.certificates[i] = d[3 : 3+certLen]
|
|
d = d[3+certLen:]
|
|
}
|
|
|
|
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
|
|
ticketFlags uint32
|
|
ticketAgeAdd uint32
|
|
ticket []byte
|
|
}
|
|
|
|
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.ticketFlags)
|
|
body.addU32(m.ticketAgeAdd)
|
|
// Send no extensions.
|
|
//
|
|
// TODO(davidben): Add an option to send a custom extension to
|
|
// test we correctly ignore unknown ones.
|
|
body.addU16(0)
|
|
}
|
|
ticket := body.addU16LengthPrefixed()
|
|
ticket.addBytes(m.ticket)
|
|
|
|
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) < 10 {
|
|
return false
|
|
}
|
|
m.ticketFlags = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
|
m.ticketAgeAdd = uint32(data[4])<<24 | uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
|
|
extsLength := int(data[8])<<8 + int(data[9])
|
|
data = data[10:]
|
|
if len(data) < extsLength {
|
|
return false
|
|
}
|
|
data = data[extsLength:]
|
|
}
|
|
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
ticketLen := int(data[0])<<8 + int(data[1])
|
|
if len(data)-2 != ticketLen {
|
|
return false
|
|
}
|
|
if m.version >= VersionTLS13 && ticketLen == 0 {
|
|
return false
|
|
}
|
|
|
|
m.ticket = data[2:]
|
|
|
|
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 := versionToWire(m.vers, true)
|
|
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 = wireToVersion(uint16(data[4])<<8|uint16(data[5]), true)
|
|
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 {
|
|
}
|
|
|
|
func (*keyUpdateMsg) marshal() []byte {
|
|
return []byte{typeKeyUpdate, 0, 0, 0}
|
|
}
|
|
|
|
func (*keyUpdateMsg) unmarshal(data []byte) bool {
|
|
return len(data) == 4
|
|
}
|
|
|
|
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
|
|
|
|
}
|