mirror of
https://github.com/safing/portmaster
synced 2025-09-01 01:59:11 +00:00
1568 lines
33 KiB
Go
1568 lines
33 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 tlslib
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
)
|
|
|
|
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])
|
|
// An SNI value may not include a
|
|
// trailing dot. See
|
|
// https://tools.ietf.org/html/rfc6066#section-3.
|
|
if strings.HasSuffix(m.ServerName, ".") {
|
|
return false
|
|
}
|
|
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 sctLen == 0 || 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
|
|
}
|