216 lines
5.1 KiB
Go
216 lines
5.1 KiB
Go
|
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||
|
//
|
||
|
// Use of this source code is governed by a BSD-style license
|
||
|
// that can be found in the LICENSE file in the root of the source
|
||
|
// tree.
|
||
|
|
||
|
package etls
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
|
||
|
"github.com/google/gopacket"
|
||
|
"github.com/google/gopacket/layers"
|
||
|
)
|
||
|
|
||
|
var LayerTypeETLS gopacket.LayerType
|
||
|
|
||
|
// ETLSType defines the type of data after the ETLS Record
|
||
|
type ETLSType uint8
|
||
|
|
||
|
// ETLSType known values.
|
||
|
const (
|
||
|
ETLSChangeCipherSpec ETLSType = 20
|
||
|
ETLSAlert ETLSType = 21
|
||
|
ETLSHandshake ETLSType = 22
|
||
|
ETLSApplicationData ETLSType = 23
|
||
|
ETLSUnknown ETLSType = 255
|
||
|
)
|
||
|
|
||
|
// String shows the register type nicely formatted
|
||
|
func (tt ETLSType) String() string {
|
||
|
switch tt {
|
||
|
default:
|
||
|
return "Unknown"
|
||
|
case ETLSChangeCipherSpec:
|
||
|
return "Change Cipher Spec"
|
||
|
case ETLSAlert:
|
||
|
return "Alert"
|
||
|
case ETLSHandshake:
|
||
|
return "Handshake"
|
||
|
case ETLSApplicationData:
|
||
|
return "Application Data"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ETLSVersion represents the ETLS version in numeric format
|
||
|
type ETLSVersion uint16
|
||
|
|
||
|
// Strings shows the ETLS version nicely formatted
|
||
|
func (tv ETLSVersion) String() string {
|
||
|
switch tv {
|
||
|
default:
|
||
|
return "Unknown"
|
||
|
case 0x0200:
|
||
|
return "SSL 2.0"
|
||
|
case 0x0300:
|
||
|
return "SSL 3.0"
|
||
|
case 0x0301:
|
||
|
return "TLS 1.0"
|
||
|
case 0x0302:
|
||
|
return "TLS 1.1"
|
||
|
case 0x0303:
|
||
|
return "TLS 1.2"
|
||
|
case 0x0304:
|
||
|
return "TLS 1.3"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ETLS is specified in RFC 5246
|
||
|
//
|
||
|
// ETLS Record Protocol
|
||
|
// 0 1 2 3 4 5 6 7 8
|
||
|
// +--+--+--+--+--+--+--+--+
|
||
|
// | Content Type |
|
||
|
// +--+--+--+--+--+--+--+--+
|
||
|
// | Version (major) |
|
||
|
// +--+--+--+--+--+--+--+--+
|
||
|
// | Version (minor) |
|
||
|
// +--+--+--+--+--+--+--+--+
|
||
|
// | Length |
|
||
|
// +--+--+--+--+--+--+--+--+
|
||
|
// | Length |
|
||
|
// +--+--+--+--+--+--+--+--+
|
||
|
|
||
|
// ETLS is actually a slide of ETLSrecord structures
|
||
|
type ETLS struct {
|
||
|
layers.BaseLayer
|
||
|
|
||
|
// ETLS Records
|
||
|
ChangeCipherSpec []ETLSChangeCipherSpecRecord
|
||
|
Handshake []ETLSHandshakeRecord
|
||
|
AppData []ETLSAppDataRecord
|
||
|
Alert []ETLSAlertRecord
|
||
|
}
|
||
|
|
||
|
// ETLSRecordHeader contains all the information that each ETLS Record types should have
|
||
|
type ETLSRecordHeader struct {
|
||
|
ContentType ETLSType
|
||
|
Version ETLSVersion
|
||
|
Length uint16
|
||
|
}
|
||
|
|
||
|
// LayerType returns gopacket.LayerTypeETLS.
|
||
|
func (t *ETLS) LayerType() gopacket.LayerType { return LayerTypeETLS }
|
||
|
|
||
|
// decodeETLS decodes the byte slice into a ETLS type. It also
|
||
|
// setups the application Layer in PacketBuilder.
|
||
|
func decodeETLS(data []byte, p gopacket.PacketBuilder) error {
|
||
|
t := &ETLS{}
|
||
|
err := t.DecodeFromBytes(data, p)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
p.AddLayer(t)
|
||
|
p.SetApplicationLayer(t)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DecodeFromBytes decodes the slice into the ETLS struct.
|
||
|
func (t *ETLS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||
|
t.BaseLayer.Contents = data
|
||
|
t.BaseLayer.Payload = nil
|
||
|
|
||
|
t.ChangeCipherSpec = t.ChangeCipherSpec[:0]
|
||
|
t.Handshake = t.Handshake[:0]
|
||
|
t.AppData = t.AppData[:0]
|
||
|
t.Alert = t.Alert[:0]
|
||
|
|
||
|
return t.decodeETLSRecords(data, df)
|
||
|
}
|
||
|
|
||
|
func (t *ETLS) decodeETLSRecords(data []byte, df gopacket.DecodeFeedback) error {
|
||
|
if len(data) < 5 {
|
||
|
df.SetTruncated()
|
||
|
return errors.New("ETLS record too short")
|
||
|
}
|
||
|
|
||
|
// since there are no further layers, the baselayer's content is
|
||
|
// pointing to this layer
|
||
|
t.BaseLayer = layers.BaseLayer{Contents: data[:len(data)]}
|
||
|
|
||
|
var h ETLSRecordHeader
|
||
|
h.ContentType = ETLSType(data[0])
|
||
|
h.Version = ETLSVersion(binary.BigEndian.Uint16(data[1:3]))
|
||
|
h.Length = binary.BigEndian.Uint16(data[3:5])
|
||
|
|
||
|
if h.ContentType.String() == "Unknown" {
|
||
|
return errors.New("Unknown ETLS record type")
|
||
|
}
|
||
|
|
||
|
hl := 5 // header length
|
||
|
tl := hl + int(h.Length)
|
||
|
if len(data) < tl {
|
||
|
df.SetTruncated()
|
||
|
return errors.New("ETLS packet length mismatch")
|
||
|
}
|
||
|
|
||
|
switch h.ContentType {
|
||
|
default:
|
||
|
return errors.New("Unknown ETLS record type")
|
||
|
case ETLSChangeCipherSpec:
|
||
|
var r ETLSChangeCipherSpecRecord
|
||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||
|
if e != nil {
|
||
|
return e
|
||
|
}
|
||
|
t.ChangeCipherSpec = append(t.ChangeCipherSpec, r)
|
||
|
case ETLSAlert:
|
||
|
var r ETLSAlertRecord
|
||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||
|
if e != nil {
|
||
|
return e
|
||
|
}
|
||
|
t.Alert = append(t.Alert, r)
|
||
|
case ETLSHandshake:
|
||
|
var r ETLSHandshakeRecord
|
||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||
|
if e != nil {
|
||
|
return e
|
||
|
}
|
||
|
t.Handshake = append(t.Handshake, r)
|
||
|
case ETLSApplicationData:
|
||
|
var r ETLSAppDataRecord
|
||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||
|
if e != nil {
|
||
|
return e
|
||
|
}
|
||
|
t.AppData = append(t.AppData, r)
|
||
|
}
|
||
|
|
||
|
if len(data) == tl {
|
||
|
return nil
|
||
|
}
|
||
|
return t.decodeETLSRecords(data[tl:len(data)], df)
|
||
|
}
|
||
|
|
||
|
// CanDecode implements gopacket.DecodingLayer.
|
||
|
func (t *ETLS) CanDecode() gopacket.LayerClass {
|
||
|
return LayerTypeETLS
|
||
|
}
|
||
|
|
||
|
// NextLayerType implements gopacket.DecodingLayer.
|
||
|
func (t *ETLS) NextLayerType() gopacket.LayerType {
|
||
|
return gopacket.LayerTypeZero
|
||
|
}
|
||
|
|
||
|
// Payload returns nil, since ETLS encrypted payload is inside ETLSAppDataRecord
|
||
|
func (t *ETLS) Payload() []byte {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
_ = gopacket.RegisterLayerType(1337, gopacket.LayerTypeMetadata{Name: "ETLS", Decoder: gopacket.DecodeFunc(decodeETLS)})
|
||
|
}
|