8b63f202ce
When the CT extension is enabled but no SCTs are present, the existing code calls "continue" which causes resizing the data byte slice to be skipped. In fact, such extensions should be rejected. Fixes #17958 Change-Id: Iad12da10d1ea72d04ae2e1012c28bb2636f06bcd Reviewed-on: https://go-review.googlesource.com/33265 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
1560 lines
32 KiB
Go
1560 lines
32 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 tls
|
|
|
|
import "bytes"
|
|
|
|
type clientHelloMsg struct {
|
|
raw []byte
|
|
vers uint16
|
|
random []byte
|
|
sessionId []byte
|
|
cipherSuites []uint16
|
|
compressionMethods []uint8
|
|
nextProtoNeg bool
|
|
serverName string
|
|
ocspStapling bool
|
|
scts bool
|
|
supportedCurves []CurveID
|
|
supportedPoints []uint8
|
|
ticketSupported bool
|
|
sessionTicket []uint8
|
|
signatureAndHashes []signatureAndHash
|
|
secureRenegotiation []byte
|
|
secureRenegotiationSupported bool
|
|
alpnProtocols []string
|
|
}
|
|
|
|
func (m *clientHelloMsg) equal(i interface{}) bool {
|
|
m1, ok := i.(*clientHelloMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
m.vers == m1.vers &&
|
|
bytes.Equal(m.random, m1.random) &&
|
|
bytes.Equal(m.sessionId, m1.sessionId) &&
|
|
eqUint16s(m.cipherSuites, m1.cipherSuites) &&
|
|
bytes.Equal(m.compressionMethods, m1.compressionMethods) &&
|
|
m.nextProtoNeg == m1.nextProtoNeg &&
|
|
m.serverName == m1.serverName &&
|
|
m.ocspStapling == m1.ocspStapling &&
|
|
m.scts == m1.scts &&
|
|
eqCurveIDs(m.supportedCurves, m1.supportedCurves) &&
|
|
bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
|
|
m.ticketSupported == m1.ticketSupported &&
|
|
bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
|
|
eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) &&
|
|
m.secureRenegotiationSupported == m1.secureRenegotiationSupported &&
|
|
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
|
|
eqStrings(m.alpnProtocols, m1.alpnProtocols)
|
|
}
|
|
|
|
func (m *clientHelloMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods)
|
|
numExtensions := 0
|
|
extensionsLength := 0
|
|
if m.nextProtoNeg {
|
|
numExtensions++
|
|
}
|
|
if m.ocspStapling {
|
|
extensionsLength += 1 + 2 + 2
|
|
numExtensions++
|
|
}
|
|
if len(m.serverName) > 0 {
|
|
extensionsLength += 5 + len(m.serverName)
|
|
numExtensions++
|
|
}
|
|
if len(m.supportedCurves) > 0 {
|
|
extensionsLength += 2 + 2*len(m.supportedCurves)
|
|
numExtensions++
|
|
}
|
|
if len(m.supportedPoints) > 0 {
|
|
extensionsLength += 1 + len(m.supportedPoints)
|
|
numExtensions++
|
|
}
|
|
if m.ticketSupported {
|
|
extensionsLength += len(m.sessionTicket)
|
|
numExtensions++
|
|
}
|
|
if len(m.signatureAndHashes) > 0 {
|
|
extensionsLength += 2 + 2*len(m.signatureAndHashes)
|
|
numExtensions++
|
|
}
|
|
if m.secureRenegotiationSupported {
|
|
extensionsLength += 1 + len(m.secureRenegotiation)
|
|
numExtensions++
|
|
}
|
|
if len(m.alpnProtocols) > 0 {
|
|
extensionsLength += 2
|
|
for _, s := range m.alpnProtocols {
|
|
if l := len(s); l == 0 || l > 255 {
|
|
panic("invalid ALPN protocol")
|
|
}
|
|
extensionsLength++
|
|
extensionsLength += len(s)
|
|
}
|
|
numExtensions++
|
|
}
|
|
if m.scts {
|
|
numExtensions++
|
|
}
|
|
if numExtensions > 0 {
|
|
extensionsLength += 4 * numExtensions
|
|
length += 2 + extensionsLength
|
|
}
|
|
|
|
x := make([]byte, 4+length)
|
|
x[0] = typeClientHello
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
x[4] = uint8(m.vers >> 8)
|
|
x[5] = uint8(m.vers)
|
|
copy(x[6:38], m.random)
|
|
x[38] = uint8(len(m.sessionId))
|
|
copy(x[39:39+len(m.sessionId)], m.sessionId)
|
|
y := x[39+len(m.sessionId):]
|
|
y[0] = uint8(len(m.cipherSuites) >> 7)
|
|
y[1] = uint8(len(m.cipherSuites) << 1)
|
|
for i, suite := range m.cipherSuites {
|
|
y[2+i*2] = uint8(suite >> 8)
|
|
y[3+i*2] = uint8(suite)
|
|
}
|
|
z := y[2+len(m.cipherSuites)*2:]
|
|
z[0] = uint8(len(m.compressionMethods))
|
|
copy(z[1:], m.compressionMethods)
|
|
|
|
z = z[1+len(m.compressionMethods):]
|
|
if numExtensions > 0 {
|
|
z[0] = byte(extensionsLength >> 8)
|
|
z[1] = byte(extensionsLength)
|
|
z = z[2:]
|
|
}
|
|
if m.nextProtoNeg {
|
|
z[0] = byte(extensionNextProtoNeg >> 8)
|
|
z[1] = byte(extensionNextProtoNeg & 0xff)
|
|
// The length is always 0
|
|
z = z[4:]
|
|
}
|
|
if len(m.serverName) > 0 {
|
|
z[0] = byte(extensionServerName >> 8)
|
|
z[1] = byte(extensionServerName & 0xff)
|
|
l := len(m.serverName) + 5
|
|
z[2] = byte(l >> 8)
|
|
z[3] = byte(l)
|
|
z = z[4:]
|
|
|
|
// 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;
|
|
|
|
z[0] = byte((len(m.serverName) + 3) >> 8)
|
|
z[1] = byte(len(m.serverName) + 3)
|
|
z[3] = byte(len(m.serverName) >> 8)
|
|
z[4] = byte(len(m.serverName))
|
|
copy(z[5:], []byte(m.serverName))
|
|
z = z[l:]
|
|
}
|
|
if m.ocspStapling {
|
|
// RFC 4366, section 3.6
|
|
z[0] = byte(extensionStatusRequest >> 8)
|
|
z[1] = byte(extensionStatusRequest)
|
|
z[2] = 0
|
|
z[3] = 5
|
|
z[4] = 1 // OCSP type
|
|
// Two zero valued uint16s for the two lengths.
|
|
z = z[9:]
|
|
}
|
|
if len(m.supportedCurves) > 0 {
|
|
// http://tools.ietf.org/html/rfc4492#section-5.5.1
|
|
z[0] = byte(extensionSupportedCurves >> 8)
|
|
z[1] = byte(extensionSupportedCurves)
|
|
l := 2 + 2*len(m.supportedCurves)
|
|
z[2] = byte(l >> 8)
|
|
z[3] = byte(l)
|
|
l -= 2
|
|
z[4] = byte(l >> 8)
|
|
z[5] = byte(l)
|
|
z = z[6:]
|
|
for _, curve := range m.supportedCurves {
|
|
z[0] = byte(curve >> 8)
|
|
z[1] = byte(curve)
|
|
z = z[2:]
|
|
}
|
|
}
|
|
if len(m.supportedPoints) > 0 {
|
|
// http://tools.ietf.org/html/rfc4492#section-5.5.2
|
|
z[0] = byte(extensionSupportedPoints >> 8)
|
|
z[1] = byte(extensionSupportedPoints)
|
|
l := 1 + len(m.supportedPoints)
|
|
z[2] = byte(l >> 8)
|
|
z[3] = byte(l)
|
|
l--
|
|
z[4] = byte(l)
|
|
z = z[5:]
|
|
for _, pointFormat := range m.supportedPoints {
|
|
z[0] = pointFormat
|
|
z = z[1:]
|
|
}
|
|
}
|
|
if m.ticketSupported {
|
|
// http://tools.ietf.org/html/rfc5077#section-3.2
|
|
z[0] = byte(extensionSessionTicket >> 8)
|
|
z[1] = byte(extensionSessionTicket)
|
|
l := len(m.sessionTicket)
|
|
z[2] = byte(l >> 8)
|
|
z[3] = byte(l)
|
|
z = z[4:]
|
|
copy(z, m.sessionTicket)
|
|
z = z[len(m.sessionTicket):]
|
|
}
|
|
if len(m.signatureAndHashes) > 0 {
|
|
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
|
z[0] = byte(extensionSignatureAlgorithms >> 8)
|
|
z[1] = byte(extensionSignatureAlgorithms)
|
|
l := 2 + 2*len(m.signatureAndHashes)
|
|
z[2] = byte(l >> 8)
|
|
z[3] = byte(l)
|
|
z = z[4:]
|
|
|
|
l -= 2
|
|
z[0] = byte(l >> 8)
|
|
z[1] = byte(l)
|
|
z = z[2:]
|
|
for _, sigAndHash := range m.signatureAndHashes {
|
|
z[0] = sigAndHash.hash
|
|
z[1] = sigAndHash.signature
|
|
z = z[2:]
|
|
}
|
|
}
|
|
if m.secureRenegotiationSupported {
|
|
z[0] = byte(extensionRenegotiationInfo >> 8)
|
|
z[1] = byte(extensionRenegotiationInfo & 0xff)
|
|
z[2] = 0
|
|
z[3] = byte(len(m.secureRenegotiation) + 1)
|
|
z[4] = byte(len(m.secureRenegotiation))
|
|
z = z[5:]
|
|
copy(z, m.secureRenegotiation)
|
|
z = z[len(m.secureRenegotiation):]
|
|
}
|
|
if len(m.alpnProtocols) > 0 {
|
|
z[0] = byte(extensionALPN >> 8)
|
|
z[1] = byte(extensionALPN & 0xff)
|
|
lengths := z[2:]
|
|
z = z[6:]
|
|
|
|
stringsLength := 0
|
|
for _, s := range m.alpnProtocols {
|
|
l := len(s)
|
|
z[0] = byte(l)
|
|
copy(z[1:], s)
|
|
z = z[1+l:]
|
|
stringsLength += 1 + l
|
|
}
|
|
|
|
lengths[2] = byte(stringsLength >> 8)
|
|
lengths[3] = byte(stringsLength)
|
|
stringsLength += 2
|
|
lengths[0] = byte(stringsLength >> 8)
|
|
lengths[1] = byte(stringsLength)
|
|
}
|
|
if m.scts {
|
|
// https://tools.ietf.org/html/rfc6962#section-3.3.1
|
|
z[0] = byte(extensionSCT >> 8)
|
|
z[1] = byte(extensionSCT)
|
|
// zero uint16 for the zero-length extension_data
|
|
z = z[4:]
|
|
}
|
|
|
|
m.raw = x
|
|
|
|
return x
|
|
}
|
|
|
|
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 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.secureRenegotiationSupported = true
|
|
}
|
|
}
|
|
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.ticketSupported = false
|
|
m.sessionTicket = nil
|
|
m.signatureAndHashes = nil
|
|
m.alpnProtocols = nil
|
|
m.scts = false
|
|
|
|
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:
|
|
d := data[:length]
|
|
if len(d) < 2 {
|
|
return false
|
|
}
|
|
namesLen := int(d[0])<<8 | int(d[1])
|
|
d = d[2:]
|
|
if len(d) != namesLen {
|
|
return false
|
|
}
|
|
for len(d) > 0 {
|
|
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[: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 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.signatureAndHashes = make([]signatureAndHash, n)
|
|
for i := range m.signatureAndHashes {
|
|
m.signatureAndHashes[i].hash = d[0]
|
|
m.signatureAndHashes[i].signature = d[1]
|
|
d = d[2:]
|
|
}
|
|
case extensionRenegotiationInfo:
|
|
if length == 0 {
|
|
return false
|
|
}
|
|
d := data[:length]
|
|
l := int(d[0])
|
|
d = d[1:]
|
|
if l != len(d) {
|
|
return false
|
|
}
|
|
|
|
m.secureRenegotiation = d
|
|
m.secureRenegotiationSupported = true
|
|
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 extensionSCT:
|
|
m.scts = true
|
|
if length != 0 {
|
|
return false
|
|
}
|
|
}
|
|
data = data[length:]
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type serverHelloMsg struct {
|
|
raw []byte
|
|
vers uint16
|
|
random []byte
|
|
sessionId []byte
|
|
cipherSuite uint16
|
|
compressionMethod uint8
|
|
nextProtoNeg bool
|
|
nextProtos []string
|
|
ocspStapling bool
|
|
scts [][]byte
|
|
ticketSupported bool
|
|
secureRenegotiation []byte
|
|
secureRenegotiationSupported bool
|
|
alpnProtocol string
|
|
}
|
|
|
|
func (m *serverHelloMsg) equal(i interface{}) bool {
|
|
m1, ok := i.(*serverHelloMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
if len(m.scts) != len(m1.scts) {
|
|
return false
|
|
}
|
|
for i, sct := range m.scts {
|
|
if !bytes.Equal(sct, m1.scts[i]) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
m.vers == m1.vers &&
|
|
bytes.Equal(m.random, m1.random) &&
|
|
bytes.Equal(m.sessionId, m1.sessionId) &&
|
|
m.cipherSuite == m1.cipherSuite &&
|
|
m.compressionMethod == m1.compressionMethod &&
|
|
m.nextProtoNeg == m1.nextProtoNeg &&
|
|
eqStrings(m.nextProtos, m1.nextProtos) &&
|
|
m.ocspStapling == m1.ocspStapling &&
|
|
m.ticketSupported == m1.ticketSupported &&
|
|
m.secureRenegotiationSupported == m1.secureRenegotiationSupported &&
|
|
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
|
|
m.alpnProtocol == m1.alpnProtocol
|
|
}
|
|
|
|
func (m *serverHelloMsg) marshal() []byte {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
length := 38 + len(m.sessionId)
|
|
numExtensions := 0
|
|
extensionsLength := 0
|
|
|
|
nextProtoLen := 0
|
|
if m.nextProtoNeg {
|
|
numExtensions++
|
|
for _, v := range m.nextProtos {
|
|
nextProtoLen += len(v)
|
|
}
|
|
nextProtoLen += len(m.nextProtos)
|
|
extensionsLength += nextProtoLen
|
|
}
|
|
if m.ocspStapling {
|
|
numExtensions++
|
|
}
|
|
if m.ticketSupported {
|
|
numExtensions++
|
|
}
|
|
if m.secureRenegotiationSupported {
|
|
extensionsLength += 1 + len(m.secureRenegotiation)
|
|
numExtensions++
|
|
}
|
|
if alpnLen := len(m.alpnProtocol); alpnLen > 0 {
|
|
if alpnLen >= 256 {
|
|
panic("invalid ALPN protocol")
|
|
}
|
|
extensionsLength += 2 + 1 + alpnLen
|
|
numExtensions++
|
|
}
|
|
sctLen := 0
|
|
if len(m.scts) > 0 {
|
|
for _, sct := range m.scts {
|
|
sctLen += len(sct) + 2
|
|
}
|
|
extensionsLength += 2 + sctLen
|
|
numExtensions++
|
|
}
|
|
|
|
if numExtensions > 0 {
|
|
extensionsLength += 4 * numExtensions
|
|
length += 2 + extensionsLength
|
|
}
|
|
|
|
x := make([]byte, 4+length)
|
|
x[0] = typeServerHello
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
x[4] = uint8(m.vers >> 8)
|
|
x[5] = uint8(m.vers)
|
|
copy(x[6:38], m.random)
|
|
x[38] = uint8(len(m.sessionId))
|
|
copy(x[39:39+len(m.sessionId)], m.sessionId)
|
|
z := x[39+len(m.sessionId):]
|
|
z[0] = uint8(m.cipherSuite >> 8)
|
|
z[1] = uint8(m.cipherSuite)
|
|
z[2] = m.compressionMethod
|
|
|
|
z = z[3:]
|
|
if numExtensions > 0 {
|
|
z[0] = byte(extensionsLength >> 8)
|
|
z[1] = byte(extensionsLength)
|
|
z = z[2:]
|
|
}
|
|
if m.nextProtoNeg {
|
|
z[0] = byte(extensionNextProtoNeg >> 8)
|
|
z[1] = byte(extensionNextProtoNeg & 0xff)
|
|
z[2] = byte(nextProtoLen >> 8)
|
|
z[3] = byte(nextProtoLen)
|
|
z = z[4:]
|
|
|
|
for _, v := range m.nextProtos {
|
|
l := len(v)
|
|
if l > 255 {
|
|
l = 255
|
|
}
|
|
z[0] = byte(l)
|
|
copy(z[1:], []byte(v[0:l]))
|
|
z = z[1+l:]
|
|
}
|
|
}
|
|
if m.ocspStapling {
|
|
z[0] = byte(extensionStatusRequest >> 8)
|
|
z[1] = byte(extensionStatusRequest)
|
|
z = z[4:]
|
|
}
|
|
if m.ticketSupported {
|
|
z[0] = byte(extensionSessionTicket >> 8)
|
|
z[1] = byte(extensionSessionTicket)
|
|
z = z[4:]
|
|
}
|
|
if m.secureRenegotiationSupported {
|
|
z[0] = byte(extensionRenegotiationInfo >> 8)
|
|
z[1] = byte(extensionRenegotiationInfo & 0xff)
|
|
z[2] = 0
|
|
z[3] = byte(len(m.secureRenegotiation) + 1)
|
|
z[4] = byte(len(m.secureRenegotiation))
|
|
z = z[5:]
|
|
copy(z, m.secureRenegotiation)
|
|
z = z[len(m.secureRenegotiation):]
|
|
}
|
|
if alpnLen := len(m.alpnProtocol); alpnLen > 0 {
|
|
z[0] = byte(extensionALPN >> 8)
|
|
z[1] = byte(extensionALPN & 0xff)
|
|
l := 2 + 1 + alpnLen
|
|
z[2] = byte(l >> 8)
|
|
z[3] = byte(l)
|
|
l -= 2
|
|
z[4] = byte(l >> 8)
|
|
z[5] = byte(l)
|
|
l -= 1
|
|
z[6] = byte(l)
|
|
copy(z[7:], []byte(m.alpnProtocol))
|
|
z = z[7+alpnLen:]
|
|
}
|
|
if sctLen > 0 {
|
|
z[0] = byte(extensionSCT >> 8)
|
|
z[1] = byte(extensionSCT)
|
|
l := sctLen + 2
|
|
z[2] = byte(l >> 8)
|
|
z[3] = byte(l)
|
|
z[4] = byte(sctLen >> 8)
|
|
z[5] = byte(sctLen)
|
|
|
|
z = z[6:]
|
|
for _, sct := range m.scts {
|
|
z[0] = byte(len(sct) >> 8)
|
|
z[1] = byte(len(sct))
|
|
copy(z[2:], sct)
|
|
z = z[len(sct)+2:]
|
|
}
|
|
}
|
|
|
|
m.raw = x
|
|
|
|
return x
|
|
}
|
|
|
|
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])
|
|
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 len(data) < 3 {
|
|
return false
|
|
}
|
|
m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
|
|
m.compressionMethod = data[2]
|
|
data = data[3:]
|
|
|
|
m.nextProtoNeg = false
|
|
m.nextProtos = nil
|
|
m.ocspStapling = false
|
|
m.scts = nil
|
|
m.ticketSupported = false
|
|
m.alpnProtocol = ""
|
|
|
|
if len(data) == 0 {
|
|
// ServerHello 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 len(data) != extensionsLength {
|
|
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 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 == 0 {
|
|
return false
|
|
}
|
|
d := data[:length]
|
|
l := int(d[0])
|
|
d = d[1:]
|
|
if l != len(d) {
|
|
return false
|
|
}
|
|
|
|
m.secureRenegotiation = d
|
|
m.secureRenegotiationSupported = true
|
|
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:]
|
|
if len(d) == 0 {
|
|
// ALPN protocols must not be empty.
|
|
return false
|
|
}
|
|
m.alpnProtocol = string(d)
|
|
case extensionSCT:
|
|
d := data[:length]
|
|
|
|
if len(d) < 2 {
|
|
return false
|
|
}
|
|
l := int(d[0])<<8 | int(d[1])
|
|
d = d[2:]
|
|
if len(d) != l || l == 0 {
|
|
return false
|
|
}
|
|
|
|
m.scts = make([][]byte, 0, 3)
|
|
for len(d) != 0 {
|
|
if len(d) < 2 {
|
|
return false
|
|
}
|
|
sctLen := int(d[0])<<8 | int(d[1])
|
|
d = d[2:]
|
|
if len(d) < sctLen {
|
|
return false
|
|
}
|
|
m.scts = append(m.scts, d[:sctLen])
|
|
d = d[sctLen:]
|
|
}
|
|
}
|
|
data = data[length:]
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type certificateMsg struct {
|
|
raw []byte
|
|
certificates [][]byte
|
|
}
|
|
|
|
func (m *certificateMsg) equal(i interface{}) bool {
|
|
m1, ok := i.(*certificateMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
eqByteSlices(m.certificates, m1.certificates)
|
|
}
|
|
|
|
func (m *certificateMsg) marshal() (x []byte) {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
var i int
|
|
for _, slice := range m.certificates {
|
|
i += len(slice)
|
|
}
|
|
|
|
length := 3 + 3*len(m.certificates) + i
|
|
x = make([]byte, 4+length)
|
|
x[0] = typeCertificate
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
|
|
certificateOctets := length - 3
|
|
x[4] = uint8(certificateOctets >> 16)
|
|
x[5] = uint8(certificateOctets >> 8)
|
|
x[6] = uint8(certificateOctets)
|
|
|
|
y := x[7:]
|
|
for _, slice := range m.certificates {
|
|
y[0] = uint8(len(slice) >> 16)
|
|
y[1] = uint8(len(slice) >> 8)
|
|
y[2] = uint8(len(slice))
|
|
copy(y[3:], slice)
|
|
y = y[3+len(slice):]
|
|
}
|
|
|
|
m.raw = x
|
|
return
|
|
}
|
|
|
|
func (m *certificateMsg) unmarshal(data []byte) bool {
|
|
if len(data) < 7 {
|
|
return false
|
|
}
|
|
|
|
m.raw = data
|
|
certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6])
|
|
if uint32(len(data)) != certsLen+7 {
|
|
return false
|
|
}
|
|
|
|
numCerts := 0
|
|
d := data[7:]
|
|
for certsLen > 0 {
|
|
if len(d) < 4 {
|
|
return false
|
|
}
|
|
certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
|
|
if uint32(len(d)) < 3+certLen {
|
|
return false
|
|
}
|
|
d = d[3+certLen:]
|
|
certsLen -= 3 + certLen
|
|
numCerts++
|
|
}
|
|
|
|
m.certificates = make([][]byte, numCerts)
|
|
d = data[7:]
|
|
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) equal(i interface{}) bool {
|
|
m1, ok := i.(*serverKeyExchangeMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
bytes.Equal(m.key, m1.key)
|
|
}
|
|
|
|
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) equal(i interface{}) bool {
|
|
m1, ok := i.(*certificateStatusMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
m.statusType == m1.statusType &&
|
|
bytes.Equal(m.response, m1.response)
|
|
}
|
|
|
|
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) equal(i interface{}) bool {
|
|
_, ok := i.(*serverHelloDoneMsg)
|
|
return ok
|
|
}
|
|
|
|
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) equal(i interface{}) bool {
|
|
m1, ok := i.(*clientKeyExchangeMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
bytes.Equal(m.ciphertext, m1.ciphertext)
|
|
}
|
|
|
|
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) equal(i interface{}) bool {
|
|
m1, ok := i.(*finishedMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
bytes.Equal(m.verifyData, m1.verifyData)
|
|
}
|
|
|
|
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) equal(i interface{}) bool {
|
|
m1, ok := i.(*nextProtoMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
m.proto == m1.proto
|
|
}
|
|
|
|
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
|
|
// hasSignatureAndHash indicates whether this message includes a list
|
|
// of signature and hash functions. This change was introduced with TLS
|
|
// 1.2.
|
|
hasSignatureAndHash bool
|
|
|
|
certificateTypes []byte
|
|
signatureAndHashes []signatureAndHash
|
|
certificateAuthorities [][]byte
|
|
}
|
|
|
|
func (m *certificateRequestMsg) equal(i interface{}) bool {
|
|
m1, ok := i.(*certificateRequestMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
bytes.Equal(m.certificateTypes, m1.certificateTypes) &&
|
|
eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) &&
|
|
eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
|
|
}
|
|
|
|
func (m *certificateRequestMsg) marshal() (x []byte) {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
// See http://tools.ietf.org/html/rfc4346#section-7.4.4
|
|
length := 1 + len(m.certificateTypes) + 2
|
|
casLength := 0
|
|
for _, ca := range m.certificateAuthorities {
|
|
casLength += 2 + len(ca)
|
|
}
|
|
length += casLength
|
|
|
|
if m.hasSignatureAndHash {
|
|
length += 2 + 2*len(m.signatureAndHashes)
|
|
}
|
|
|
|
x = make([]byte, 4+length)
|
|
x[0] = typeCertificateRequest
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
|
|
x[4] = uint8(len(m.certificateTypes))
|
|
|
|
copy(x[5:], m.certificateTypes)
|
|
y := x[5+len(m.certificateTypes):]
|
|
|
|
if m.hasSignatureAndHash {
|
|
n := len(m.signatureAndHashes) * 2
|
|
y[0] = uint8(n >> 8)
|
|
y[1] = uint8(n)
|
|
y = y[2:]
|
|
for _, sigAndHash := range m.signatureAndHashes {
|
|
y[0] = sigAndHash.hash
|
|
y[1] = sigAndHash.signature
|
|
y = y[2:]
|
|
}
|
|
}
|
|
|
|
y[0] = uint8(casLength >> 8)
|
|
y[1] = uint8(casLength)
|
|
y = y[2:]
|
|
for _, ca := range m.certificateAuthorities {
|
|
y[0] = uint8(len(ca) >> 8)
|
|
y[1] = uint8(len(ca))
|
|
y = y[2:]
|
|
copy(y, ca)
|
|
y = y[len(ca):]
|
|
}
|
|
|
|
m.raw = x
|
|
return
|
|
}
|
|
|
|
func (m *certificateRequestMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
|
|
if len(data) < 5 {
|
|
return false
|
|
}
|
|
|
|
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
|
if uint32(len(data))-4 != length {
|
|
return false
|
|
}
|
|
|
|
numCertTypes := int(data[4])
|
|
data = data[5:]
|
|
if numCertTypes == 0 || len(data) <= numCertTypes {
|
|
return false
|
|
}
|
|
|
|
m.certificateTypes = make([]byte, numCertTypes)
|
|
if copy(m.certificateTypes, data) != numCertTypes {
|
|
return false
|
|
}
|
|
|
|
data = data[numCertTypes:]
|
|
|
|
if m.hasSignatureAndHash {
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
|
|
data = data[2:]
|
|
if sigAndHashLen&1 != 0 {
|
|
return false
|
|
}
|
|
if len(data) < int(sigAndHashLen) {
|
|
return false
|
|
}
|
|
numSigAndHash := sigAndHashLen / 2
|
|
m.signatureAndHashes = make([]signatureAndHash, numSigAndHash)
|
|
for i := range m.signatureAndHashes {
|
|
m.signatureAndHashes[i].hash = data[0]
|
|
m.signatureAndHashes[i].signature = 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:]
|
|
}
|
|
|
|
return len(data) == 0
|
|
}
|
|
|
|
type certificateVerifyMsg struct {
|
|
raw []byte
|
|
hasSignatureAndHash bool
|
|
signatureAndHash signatureAndHash
|
|
signature []byte
|
|
}
|
|
|
|
func (m *certificateVerifyMsg) equal(i interface{}) bool {
|
|
m1, ok := i.(*certificateVerifyMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
m.hasSignatureAndHash == m1.hasSignatureAndHash &&
|
|
m.signatureAndHash.hash == m1.signatureAndHash.hash &&
|
|
m.signatureAndHash.signature == m1.signatureAndHash.signature &&
|
|
bytes.Equal(m.signature, m1.signature)
|
|
}
|
|
|
|
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.hasSignatureAndHash {
|
|
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.hasSignatureAndHash {
|
|
y[0] = m.signatureAndHash.hash
|
|
y[1] = m.signatureAndHash.signature
|
|
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.hasSignatureAndHash {
|
|
m.signatureAndHash.hash = data[0]
|
|
m.signatureAndHash.signature = 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
|
|
ticket []byte
|
|
}
|
|
|
|
func (m *newSessionTicketMsg) equal(i interface{}) bool {
|
|
m1, ok := i.(*newSessionTicketMsg)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(m.raw, m1.raw) &&
|
|
bytes.Equal(m.ticket, m1.ticket)
|
|
}
|
|
|
|
func (m *newSessionTicketMsg) marshal() (x []byte) {
|
|
if m.raw != nil {
|
|
return m.raw
|
|
}
|
|
|
|
// See http://tools.ietf.org/html/rfc5077#section-3.3
|
|
ticketLen := len(m.ticket)
|
|
length := 2 + 4 + ticketLen
|
|
x = make([]byte, 4+length)
|
|
x[0] = typeNewSessionTicket
|
|
x[1] = uint8(length >> 16)
|
|
x[2] = uint8(length >> 8)
|
|
x[3] = uint8(length)
|
|
x[8] = uint8(ticketLen >> 8)
|
|
x[9] = uint8(ticketLen)
|
|
copy(x[10:], m.ticket)
|
|
|
|
m.raw = x
|
|
|
|
return
|
|
}
|
|
|
|
func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
|
|
m.raw = data
|
|
|
|
if len(data) < 10 {
|
|
return false
|
|
}
|
|
|
|
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
|
|
if uint32(len(data))-4 != length {
|
|
return false
|
|
}
|
|
|
|
ticketLen := int(data[8])<<8 + int(data[9])
|
|
if len(data)-10 != ticketLen {
|
|
return false
|
|
}
|
|
|
|
m.ticket = data[10:]
|
|
|
|
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
|
|
}
|
|
|
|
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 eqSignatureAndHashes(x, y []signatureAndHash) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, v := range x {
|
|
v2 := y[i]
|
|
if v.hash != v2.hash || v.signature != v2.signature {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|