mirror of
https://github.com/cbeuw/Cloak.git
synced 2024-11-09 19:10:44 +00:00
164 lines
4.8 KiB
Go
164 lines
4.8 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/cbeuw/Cloak/internal/util"
|
|
)
|
|
|
|
// ClientHello contains every field in a ClientHello message
|
|
type ClientHello struct {
|
|
handshakeType byte
|
|
length int
|
|
clientVersion []byte
|
|
random []byte
|
|
sessionIdLen int
|
|
sessionId []byte
|
|
cipherSuitesLen int
|
|
cipherSuites []byte
|
|
compressionMethodsLen int
|
|
compressionMethods []byte
|
|
extensionsLen int
|
|
extensions map[[2]byte][]byte
|
|
}
|
|
|
|
func parseExtensions(input []byte) (ret map[[2]byte][]byte, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err = errors.New("Malformed Extensions")
|
|
}
|
|
}()
|
|
pointer := 0
|
|
totalLen := len(input)
|
|
ret = make(map[[2]byte][]byte)
|
|
for pointer < totalLen {
|
|
var typ [2]byte
|
|
copy(typ[:], input[pointer:pointer+2])
|
|
pointer += 2
|
|
length := util.BtoInt(input[pointer : pointer+2])
|
|
pointer += 2
|
|
data := input[pointer : pointer+length]
|
|
pointer += length
|
|
ret[typ] = data
|
|
}
|
|
return ret, err
|
|
}
|
|
|
|
// AddRecordLayer adds record layer to data
|
|
func AddRecordLayer(input []byte, typ []byte, ver []byte) []byte {
|
|
length := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(length, uint16(len(input)))
|
|
ret := make([]byte, 5+len(input))
|
|
copy(ret[0:1], typ)
|
|
copy(ret[1:3], ver)
|
|
copy(ret[3:5], length)
|
|
copy(ret[5:], input)
|
|
return ret
|
|
}
|
|
|
|
// PeelRecordLayer peels off the record layer
|
|
func PeelRecordLayer(data []byte) []byte {
|
|
ret := data[5:]
|
|
return ret
|
|
}
|
|
|
|
// ParseClientHello parses everything on top of the TLS layer
|
|
// (including the record layer) into ClientHello type
|
|
func ParseClientHello(data []byte) (ret *ClientHello, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err = errors.New("Malformed ClientHello")
|
|
}
|
|
}()
|
|
data = PeelRecordLayer(data)
|
|
pointer := 0
|
|
// Handshake Type
|
|
handshakeType := data[pointer]
|
|
if handshakeType != 0x01 {
|
|
return ret, errors.New("Not a ClientHello")
|
|
}
|
|
pointer += 1
|
|
// Length
|
|
length := util.BtoInt(data[pointer : pointer+3])
|
|
pointer += 3
|
|
if length != len(data[pointer:]) {
|
|
return ret, errors.New("Hello length doesn't match")
|
|
}
|
|
// Client Version
|
|
clientVersion := data[pointer : pointer+2]
|
|
pointer += 2
|
|
// Random
|
|
random := data[pointer : pointer+32]
|
|
pointer += 32
|
|
// Session ID
|
|
sessionIdLen := int(data[pointer])
|
|
pointer += 1
|
|
sessionId := data[pointer : pointer+sessionIdLen]
|
|
pointer += sessionIdLen
|
|
// Cipher Suites
|
|
cipherSuitesLen := util.BtoInt(data[pointer : pointer+2])
|
|
pointer += 2
|
|
cipherSuites := data[pointer : pointer+cipherSuitesLen]
|
|
pointer += cipherSuitesLen
|
|
// Compression Methods
|
|
compressionMethodsLen := int(data[pointer])
|
|
pointer += 1
|
|
compressionMethods := data[pointer : pointer+compressionMethodsLen]
|
|
pointer += compressionMethodsLen
|
|
// Extensions
|
|
extensionsLen := util.BtoInt(data[pointer : pointer+2])
|
|
pointer += 2
|
|
extensions, err := parseExtensions(data[pointer:])
|
|
ret = &ClientHello{
|
|
handshakeType,
|
|
length,
|
|
clientVersion,
|
|
random,
|
|
sessionIdLen,
|
|
sessionId,
|
|
cipherSuitesLen,
|
|
cipherSuites,
|
|
compressionMethodsLen,
|
|
compressionMethods,
|
|
extensionsLen,
|
|
extensions,
|
|
}
|
|
return
|
|
}
|
|
|
|
func composeServerHello(ch *ClientHello) []byte {
|
|
var serverHello [10][]byte
|
|
serverHello[0] = []byte{0x02} // handshake type
|
|
serverHello[1] = []byte{0x00, 0x00, 0x4d} // length 77
|
|
serverHello[2] = []byte{0x03, 0x03} // server version
|
|
serverHello[3] = util.PsudoRandBytes(32, time.Now().UnixNano()) // random
|
|
serverHello[4] = []byte{0x20} // session id length 32
|
|
serverHello[5] = ch.sessionId // session id
|
|
serverHello[6] = []byte{0xc0, 0x30} // cipher suite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
|
serverHello[7] = []byte{0x00} // compression method null
|
|
serverHello[8] = []byte{0x00, 0x05} // extensions length 5
|
|
serverHello[9] = []byte{0xff, 0x01, 0x00, 0x01, 0x00} // extensions renegotiation_info
|
|
ret := []byte{}
|
|
for i := 0; i < 10; i++ {
|
|
ret = append(ret, serverHello[i]...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// ComposeReply composes the ServerHello, ChangeCipherSpec and Finished messages
|
|
// together with their respective record layers into one byte slice. The content
|
|
// of these messages are random and useless for this plugin
|
|
func ComposeReply(ch *ClientHello) []byte {
|
|
TLS12 := []byte{0x03, 0x03}
|
|
shBytes := AddRecordLayer(composeServerHello(ch), []byte{0x16}, TLS12)
|
|
ccsBytes := AddRecordLayer([]byte{0x01}, []byte{0x14}, TLS12)
|
|
finished := make([]byte, 64)
|
|
finished = util.PsudoRandBytes(40, time.Now().UnixNano())
|
|
fBytes := AddRecordLayer(finished, []byte{0x16}, TLS12)
|
|
ret := append(shBytes, ccsBytes...)
|
|
ret = append(ret, fBytes...)
|
|
return ret
|
|
}
|