239 lines
5.0 KiB
Go
239 lines
5.0 KiB
Go
package dissector
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
handshakeHeaderLen = 4
|
|
)
|
|
|
|
const (
|
|
HelloRequest = 0
|
|
ClientHello = 1
|
|
ServerHello = 2
|
|
)
|
|
|
|
type Random struct {
|
|
Time uint32
|
|
Opaque [28]byte
|
|
}
|
|
|
|
type CipherSuite uint16
|
|
|
|
type CompressionMethod uint8
|
|
|
|
type ClientHelloHandshake struct {
|
|
Version Version
|
|
Random Random
|
|
SessionID []byte
|
|
CipherSuites []CipherSuite
|
|
CompressionMethods []CompressionMethod
|
|
Extensions []Extension
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) Encode() (data []byte, err error) {
|
|
buf := new(bytes.Buffer)
|
|
if _, err = h.WriteTo(buf); err != nil {
|
|
return
|
|
}
|
|
data = buf.Bytes()
|
|
return
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) Decode(data []byte) (err error) {
|
|
_, err = h.ReadFrom(bytes.NewReader(data))
|
|
return
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) ReadFrom(r io.Reader) (n int64, err error) {
|
|
b := make([]byte, handshakeHeaderLen)
|
|
nn, err := io.ReadFull(r, b)
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if b[0] != ClientHello {
|
|
err = ErrBadType
|
|
return
|
|
}
|
|
|
|
length := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
|
|
if length < 34 { // length of version + random
|
|
err = fmt.Errorf("bad length, need at least 34 bytes, got %d", length)
|
|
return
|
|
}
|
|
|
|
b = make([]byte, length)
|
|
nn, err = io.ReadFull(r, b)
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return
|
|
}
|
|
h.Version = Version(binary.BigEndian.Uint16(b[:2]))
|
|
if h.Version < tls.VersionTLS12 {
|
|
err = fmt.Errorf("bad version: only TLSv1.2 is supported")
|
|
return
|
|
}
|
|
|
|
pos := 2
|
|
h.Random.Time = binary.BigEndian.Uint32(b[pos : pos+4])
|
|
pos += 4
|
|
copy(h.Random.Opaque[:], b[pos:pos+28])
|
|
pos += 28
|
|
|
|
nn, err = h.readSession(b[pos:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
pos += nn
|
|
|
|
nn, err = h.readCipherSuites(b[pos:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
pos += nn
|
|
|
|
nn, err = h.readCompressionMethods(b[pos:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
pos += nn
|
|
|
|
nn, err = h.readExtensions(b[pos:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
// pos += nn
|
|
|
|
return
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) readSession(b []byte) (n int, err error) {
|
|
if len(b) == 0 {
|
|
err = fmt.Errorf("bad length: data too short for session")
|
|
return
|
|
}
|
|
|
|
nlen := int(b[0])
|
|
n++
|
|
if len(b) < n+nlen {
|
|
err = fmt.Errorf("bad length: malformed data for session")
|
|
}
|
|
if nlen > 0 && n+nlen <= len(b) {
|
|
h.SessionID = make([]byte, nlen)
|
|
copy(h.SessionID, b[n:n+nlen])
|
|
n += nlen
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) readCipherSuites(b []byte) (n int, err error) {
|
|
if len(b) < 2 {
|
|
err = fmt.Errorf("bad length: data too short for cipher suites")
|
|
return
|
|
}
|
|
|
|
nlen := int(binary.BigEndian.Uint16(b[:2]))
|
|
n += 2
|
|
if len(b) < n+nlen {
|
|
err = fmt.Errorf("bad length: malformed data for cipher suites")
|
|
}
|
|
for i := 0; i < nlen/2; i++ {
|
|
h.CipherSuites = append(h.CipherSuites, CipherSuite(binary.BigEndian.Uint16(b[n:n+2])))
|
|
n += 2
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) readCompressionMethods(b []byte) (n int, err error) {
|
|
if len(b) == 0 {
|
|
err = fmt.Errorf("bad length: data too short for compression methods")
|
|
return
|
|
}
|
|
nlen := int(b[0])
|
|
n++
|
|
if len(b) < n+nlen {
|
|
err = fmt.Errorf("bad length: malformed data for compression methods")
|
|
}
|
|
for i := 0; i < nlen; i++ {
|
|
h.CompressionMethods = append(h.CompressionMethods, CompressionMethod(b[n]))
|
|
n++
|
|
}
|
|
return
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) readExtensions(b []byte) (n int, err error) {
|
|
if len(b) < 2 {
|
|
err = fmt.Errorf("bad length: data too short for extensions")
|
|
return
|
|
}
|
|
nlen := int(binary.BigEndian.Uint16(b[:2]))
|
|
n += 2
|
|
if len(b) < n+nlen {
|
|
err = fmt.Errorf("bad length: malformed data for extensions")
|
|
return
|
|
}
|
|
|
|
br := bytes.NewReader(b[n:])
|
|
for br.Len() > 0 {
|
|
cn := br.Len()
|
|
var ext Extension
|
|
ext, err = ReadExtension(br)
|
|
if err != nil {
|
|
return
|
|
}
|
|
h.Extensions = append(h.Extensions, ext)
|
|
n += (cn - br.Len())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (h *ClientHelloHandshake) WriteTo(w io.Writer) (n int64, err error) {
|
|
buf := &bytes.Buffer{}
|
|
buf.WriteByte(ClientHello)
|
|
buf.Write([]byte{0, 0, 0}) // placeholder for payload length
|
|
binary.Write(buf, binary.BigEndian, h.Version)
|
|
pos := 6
|
|
binary.Write(buf, binary.BigEndian, h.Random.Time)
|
|
buf.Write(h.Random.Opaque[:])
|
|
pos += 32
|
|
buf.WriteByte(byte(len(h.SessionID)))
|
|
buf.Write(h.SessionID)
|
|
pos += (1 + len(h.SessionID))
|
|
binary.Write(buf, binary.BigEndian, uint16(len(h.CipherSuites)*2))
|
|
for _, cs := range h.CipherSuites {
|
|
binary.Write(buf, binary.BigEndian, cs)
|
|
}
|
|
pos += (2 + len(h.CipherSuites)*2)
|
|
buf.WriteByte(byte(len(h.CompressionMethods)))
|
|
for _, cm := range h.CompressionMethods {
|
|
buf.WriteByte(byte(cm))
|
|
}
|
|
pos += (1 + len(h.CompressionMethods))
|
|
buf.Write([]byte{0, 0}) // placeholder for extensions length
|
|
|
|
extLen := 0
|
|
for _, ext := range h.Extensions {
|
|
nn, _ := buf.Write(ext.Bytes())
|
|
extLen += nn
|
|
}
|
|
|
|
b := buf.Bytes()
|
|
plen := len(b) - handshakeHeaderLen
|
|
b[1], b[2], b[3] = byte((plen>>16)&0xFF), byte((plen>>8)&0xFF), byte(plen&0xFF) // payload length
|
|
b[pos], b[pos+1] = byte((extLen>>8)&0xFF), byte(extLen&0xFF) // extensions length
|
|
|
|
nn, err := w.Write(b)
|
|
n = int64(nn)
|
|
return
|
|
}
|