mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 18:34:02 +03:00
Can correctly accept faketls messages
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package clienthello
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
||||
)
|
||||
|
||||
type ClientHello struct {
|
||||
Time time.Time
|
||||
Digest [RandomLen]byte
|
||||
SessionID []byte
|
||||
}
|
||||
|
||||
func ParseHandshake(secret, handshake []byte) (ClientHello, error) {
|
||||
hello := ClientHello{}
|
||||
|
||||
if len(handshake) < MinLen {
|
||||
return hello, fmt.Errorf("lengh of handshake is too small: %d", len(handshake))
|
||||
}
|
||||
|
||||
if handshake[0] != HandshakeTypeClient {
|
||||
return hello, fmt.Errorf("unknown handshake type %#x", handshake[0])
|
||||
}
|
||||
|
||||
copy(hello.Digest[:], handshake[RandomOffset:])
|
||||
|
||||
for i := RandomOffset; i < RandomOffset+RandomLen; i++ {
|
||||
handshake[i] = 0
|
||||
}
|
||||
|
||||
rec := record.AcquireRecord()
|
||||
defer record.ReleaseRecord(rec)
|
||||
|
||||
rec.Type = record.TypeHandshake
|
||||
rec.Version = record.Version10
|
||||
rec.Payload.Write(handshake)
|
||||
|
||||
// mac is calculated for the whole record, not only
|
||||
// for the payload part
|
||||
mac := hmac.New(sha256.New, secret)
|
||||
rec.Dump(mac)
|
||||
|
||||
computedDigest := mac.Sum(nil)
|
||||
|
||||
for i := 0; i < RandomLen; i++ {
|
||||
computedDigest[i] ^= hello.Digest[i]
|
||||
}
|
||||
|
||||
for i := 0; i < RandomLen-4; i++ {
|
||||
if computedDigest[i] != 0 {
|
||||
return hello, ErrBadDigest
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := int64(binary.LittleEndian.Uint32(computedDigest[RandomLen-4:]))
|
||||
hello.Time = time.Unix(timestamp, 0)
|
||||
|
||||
hello.SessionID = make([]byte, handshake[SessionIDOffset])
|
||||
copy(hello.SessionID, handshake[SessionIDOffset+1:])
|
||||
|
||||
return hello, nil
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package clienthello
|
||||
|
||||
import "errors"
|
||||
|
||||
const (
|
||||
RandomLen = 32
|
||||
RandomOffset = 6
|
||||
SessionIDOffset = RandomOffset + RandomLen
|
||||
MinLen = SessionIDOffset + 1
|
||||
|
||||
HandshakeTypeClient = 0x01
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadDigest = errors.New("bad digest")
|
||||
ErrAntiReplayAttack = errors.New("antireplay attack was detected")
|
||||
)
|
||||
@@ -0,0 +1,66 @@
|
||||
package record
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Type uint8
|
||||
|
||||
const (
|
||||
TypeChangeCipherSpec Type = 0x14
|
||||
TypeHandshake Type = 0x16
|
||||
TypeApplicationData Type = 0x17
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeChangeCipherSpec:
|
||||
return "changeCipher(0x14)"
|
||||
case TypeHandshake:
|
||||
return "handshake(0x16)"
|
||||
case TypeApplicationData:
|
||||
return "applicationData(0x17)"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("unknown(%#x)", byte(t))
|
||||
}
|
||||
|
||||
func (t Type) Valid() error {
|
||||
switch t {
|
||||
case TypeChangeCipherSpec, TypeHandshake, TypeApplicationData:
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown type %#x", byte(t))
|
||||
}
|
||||
|
||||
type Version uint16
|
||||
|
||||
const (
|
||||
Version10 Version = 769 // 0x03 0x01
|
||||
Version11 Version = 770 // 0x03 0x02
|
||||
Version12 Version = 771 // 0x03 0x03
|
||||
Version13 Version = 772 // 0x03 0x04
|
||||
)
|
||||
|
||||
func (v Version) String() string {
|
||||
switch v {
|
||||
case Version10:
|
||||
return "tls1.0"
|
||||
case Version11:
|
||||
return "tls1.1"
|
||||
case Version12:
|
||||
return "tls1.2"
|
||||
case Version13:
|
||||
return "tls1.3"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("tls(%d)", uint16(v))
|
||||
}
|
||||
|
||||
func (v Version) Valid() error {
|
||||
switch v {
|
||||
case Version10, Version11, Version12, Version13:
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown version %d", uint16(v))
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package record
|
||||
|
||||
import "sync"
|
||||
|
||||
var recordPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Record{}
|
||||
},
|
||||
}
|
||||
|
||||
func AcquireRecord() *Record {
|
||||
return recordPool.Get().(*Record)
|
||||
}
|
||||
|
||||
func ReleaseRecord(r *Record) {
|
||||
r.Reset()
|
||||
recordPool.Put(r)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Record struct {
|
||||
Type Type
|
||||
Version Version
|
||||
Payload bytes.Buffer
|
||||
}
|
||||
|
||||
func (r *Record) String() string {
|
||||
return fmt.Sprintf("<tlsRecord(type=%v, version=%v, payload=%s)>",
|
||||
r.Type,
|
||||
r.Version,
|
||||
base64.StdEncoding.EncodeToString(r.Payload.Bytes()))
|
||||
}
|
||||
|
||||
func (r *Record) Reset() {
|
||||
r.Payload.Reset()
|
||||
}
|
||||
|
||||
func (r *Record) Read(reader io.Reader) error {
|
||||
r.Reset()
|
||||
|
||||
buf := [2]byte{}
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:1]); err != nil {
|
||||
return fmt.Errorf("cannot read type: %w", err)
|
||||
}
|
||||
|
||||
r.Type = Type(buf[0])
|
||||
if err := r.Type.Valid(); err != nil {
|
||||
return fmt.Errorf("invalid type: %w", err)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:]); err != nil {
|
||||
return fmt.Errorf("cannot read version: %w", err)
|
||||
}
|
||||
|
||||
r.Version = Version(binary.BigEndian.Uint16(buf[:]))
|
||||
if err := r.Version.Valid(); err != nil {
|
||||
return fmt.Errorf("invalid version: %w", err)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:]); err != nil {
|
||||
return fmt.Errorf("cannot read payload length: %w", err)
|
||||
}
|
||||
|
||||
length := int64(binary.BigEndian.Uint16(buf[:]))
|
||||
if _, err := io.CopyN(&r.Payload, reader, length); err != nil {
|
||||
return fmt.Errorf("cannot read payload: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Record) Dump(writer io.Writer) error {
|
||||
buf := [2]byte{byte(r.Type), 0}
|
||||
|
||||
if _, err := writer.Write(buf[:1]); err != nil {
|
||||
return fmt.Errorf("cannot dump type: %w", err)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf[:], uint16(r.Version))
|
||||
|
||||
if _, err := writer.Write(buf[:]); err != nil {
|
||||
return fmt.Errorf("cannot dump version: %w", err)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf[:], uint16(r.Payload.Len()))
|
||||
|
||||
if _, err := writer.Write(buf[:]); err != nil {
|
||||
return fmt.Errorf("cannot dump payload length: %w", err)
|
||||
}
|
||||
|
||||
if _, err := writer.Write(r.Payload.Bytes()); err != nil {
|
||||
return fmt.Errorf("cannot dump payload: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user