Cloak/internal/server/TLS.go

268 lines
7.6 KiB
Go
Raw Normal View History

2018-10-09 15:07:54 +00:00
package server
import (
2019-08-02 00:01:19 +00:00
"bytes"
"crypto/rand"
2018-10-09 15:07:54 +00:00
"encoding/binary"
2019-08-02 00:01:19 +00:00
"encoding/hex"
2018-10-09 15:07:54 +00:00
"errors"
2019-08-02 00:01:19 +00:00
"fmt"
"github.com/cbeuw/Cloak/internal/util"
2019-08-06 14:50:33 +00:00
"net"
log "github.com/sirupsen/logrus"
2018-10-09 15:07:54 +00:00
)
// 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
}
2019-01-13 22:22:55 +00:00
var u16 = binary.BigEndian.Uint16
var u32 = binary.BigEndian.Uint32
2018-10-09 15:07:54 +00:00
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
2019-01-13 22:22:55 +00:00
length := int(u16(input[pointer : pointer+2]))
2018-10-09 15:07:54 +00:00
pointer += 2
data := input[pointer : pointer+length]
pointer += length
ret[typ] = data
}
return ret, err
}
2019-08-02 00:01:19 +00:00
func parseKeyShare(input []byte) (ret []byte, err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("malformed key_share")
}
}()
totalLen := int(u16(input[0:2]))
// 2 bytes "client key share length"
pointer := 2
for pointer < totalLen {
if bytes.Equal([]byte{0x00, 0x1d}, input[pointer:pointer+2]) {
// skip "key exchange length"
pointer += 2
length := int(u16(input[pointer : pointer+2]))
pointer += 2
if length != 32 {
return nil, fmt.Errorf("key share length should be 32, instead of %v", length)
}
return input[pointer : pointer+length], nil
}
pointer += 2
length := int(u16(input[pointer : pointer+2]))
pointer += 2
_ = input[pointer : pointer+length]
pointer += length
}
return nil, errors.New("x25519 does not exist")
}
2019-08-06 14:50:33 +00:00
// addRecordLayer adds record layer to data
func addRecordLayer(input []byte, typ []byte, ver []byte) []byte {
2018-10-09 15:07:54 +00:00
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
}
2019-08-06 14:50:33 +00:00
// parseClientHello parses everything on top of the TLS layer
2018-10-09 15:07:54 +00:00
// (including the record layer) into ClientHello type
2019-08-06 14:50:33 +00:00
func parseClientHello(data []byte) (ret *ClientHello, err error) {
2018-10-09 15:07:54 +00:00
defer func() {
if r := recover(); r != nil {
err = errors.New("Malformed ClientHello")
}
}()
2019-08-03 12:26:57 +00:00
if !bytes.Equal(data[0:3], []byte{0x16, 0x03, 0x01}) {
2019-08-16 23:59:30 +00:00
return ret, errors.New("wrong TLS1.3 handshake magic bytes")
}
2019-08-03 12:26:57 +00:00
peeled := make([]byte, len(data)-5)
copy(peeled, data[5:])
2018-10-09 15:07:54 +00:00
pointer := 0
// Handshake Type
2019-08-03 12:26:57 +00:00
handshakeType := peeled[pointer]
2018-10-09 15:07:54 +00:00
if handshakeType != 0x01 {
return ret, errors.New("Not a ClientHello")
}
pointer += 1
// Length
2019-08-03 12:26:57 +00:00
length := int(u32(append([]byte{0x00}, peeled[pointer:pointer+3]...)))
2018-10-09 15:07:54 +00:00
pointer += 3
2019-08-03 12:26:57 +00:00
if length != len(peeled[pointer:]) {
2018-10-09 15:07:54 +00:00
return ret, errors.New("Hello length doesn't match")
}
// Client Version
2019-08-03 12:26:57 +00:00
clientVersion := peeled[pointer : pointer+2]
2018-10-09 15:07:54 +00:00
pointer += 2
// Random
2019-08-03 12:26:57 +00:00
random := peeled[pointer : pointer+32]
2018-10-09 15:07:54 +00:00
pointer += 32
// Session ID
2019-08-03 12:26:57 +00:00
sessionIdLen := int(peeled[pointer])
2018-10-09 15:07:54 +00:00
pointer += 1
2019-08-03 12:26:57 +00:00
sessionId := peeled[pointer : pointer+sessionIdLen]
2018-10-09 15:07:54 +00:00
pointer += sessionIdLen
// Cipher Suites
2019-08-03 12:26:57 +00:00
cipherSuitesLen := int(u16(peeled[pointer : pointer+2]))
2018-10-09 15:07:54 +00:00
pointer += 2
2019-08-03 12:26:57 +00:00
cipherSuites := peeled[pointer : pointer+cipherSuitesLen]
2018-10-09 15:07:54 +00:00
pointer += cipherSuitesLen
// Compression Methods
2019-08-03 12:26:57 +00:00
compressionMethodsLen := int(peeled[pointer])
2018-10-09 15:07:54 +00:00
pointer += 1
2019-08-03 12:26:57 +00:00
compressionMethods := peeled[pointer : pointer+compressionMethodsLen]
2018-10-09 15:07:54 +00:00
pointer += compressionMethodsLen
// Extensions
2019-08-03 12:26:57 +00:00
extensionsLen := int(u16(peeled[pointer : pointer+2]))
2018-10-09 15:07:54 +00:00
pointer += 2
2019-08-03 12:26:57 +00:00
extensions, err := parseExtensions(peeled[pointer:])
2018-10-09 15:07:54 +00:00
ret = &ClientHello{
handshakeType,
length,
clientVersion,
random,
sessionIdLen,
sessionId,
cipherSuitesLen,
cipherSuites,
compressionMethodsLen,
compressionMethods,
extensionsLen,
extensions,
}
return
}
func composeServerHello(sessionId []byte, sharedSecret []byte, sessionKey []byte) ([]byte, error) {
nonce := make([]byte, 12)
rand.Read(nonce)
encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret, sessionKey) // 32 + 16 = 48 bytes
if err != nil {
return nil, err
}
2019-08-02 00:01:19 +00:00
var serverHello [11][]byte
serverHello[0] = []byte{0x02} // handshake type
serverHello[1] = []byte{0x00, 0x00, 0x76} // length 77
serverHello[2] = []byte{0x03, 0x03} // server version
serverHello[3] = append(nonce[0:12], encryptedKey[0:20]...) // random 32 bytes
serverHello[4] = []byte{0x20} // session id length 32
serverHello[5] = 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, 0x2e} // extensions length 46
2019-08-02 00:01:19 +00:00
keyShare, _ := hex.DecodeString("00330024001d0020")
keyExchange := make([]byte, 32)
copy(keyExchange, encryptedKey[20:48])
rand.Read(keyExchange[28:32])
2019-08-02 00:01:19 +00:00
serverHello[9] = append(keyShare, keyExchange...)
serverHello[10], _ = hex.DecodeString("002b00020304")
var ret []byte
for _, s := range serverHello {
ret = append(ret, s...)
2018-10-09 15:07:54 +00:00
}
return ret, nil
2018-10-09 15:07:54 +00:00
}
2019-08-20 21:43:04 +00:00
// composeReply composes the ServerHello, ChangeCipherSpec and an ApplicationData messages
// together with their respective record layers into one byte slice.
func composeReply(ch *ClientHello, sharedSecret []byte, sessionKey []byte) ([]byte, error) {
2018-10-09 15:07:54 +00:00
TLS12 := []byte{0x03, 0x03}
sh, err := composeServerHello(ch.sessionId, sharedSecret, sessionKey)
if err != nil {
return nil, err
}
shBytes := addRecordLayer(sh, []byte{0x16}, TLS12)
2019-08-06 14:50:33 +00:00
ccsBytes := addRecordLayer([]byte{0x01}, []byte{0x14}, TLS12)
2019-08-16 22:20:24 +00:00
cert := make([]byte, 68) // TODO: add some different lengths maybe?
rand.Read(cert)
encryptedCertBytes := addRecordLayer(cert, []byte{0x17}, TLS12)
2018-10-09 15:07:54 +00:00
ret := append(shBytes, ccsBytes...)
ret = append(ret, encryptedCertBytes...)
return ret, nil
2018-10-09 15:07:54 +00:00
}
2019-08-06 14:50:33 +00:00
var ErrBadClientHello = errors.New("non (or malformed) ClientHello")
var ErrNotCloak = errors.New("TLS but non-Cloak ClientHello")
2019-08-16 22:56:46 +00:00
var ErrReplay = errors.New("duplicate random")
2019-08-06 14:50:33 +00:00
var ErrBadProxyMethod = errors.New("invalid proxy method")
2019-08-20 21:43:04 +00:00
// PrepareConnection checks if the first packet of data is ClientHello, and checks if it was from a Cloak client
// if it is from a Cloak client, it returns the ClientInfo with the decrypted fields. It doesn't check if the user
// is authorised. It also returns a finisher callback function to be called when the caller wishes to proceed with
// the handshake
2019-08-14 10:48:32 +00:00
func PrepareConnection(firstPacket []byte, sta *State, conn net.Conn) (info ClientInfo, finisher func([]byte) error, err error) {
2019-08-06 14:50:33 +00:00
ch, err := parseClientHello(firstPacket)
if err != nil {
log.Debug(err)
err = ErrBadClientHello
return
}
2019-08-16 22:56:46 +00:00
if sta.registerRandom(ch.random) {
err = ErrReplay
return
}
2019-08-06 14:50:33 +00:00
var sharedSecret []byte
2019-08-16 22:56:46 +00:00
info, sharedSecret, err = touchStone(ch, sta.staticPv, sta.Now)
2019-08-06 14:50:33 +00:00
if err != nil {
log.Debug(err)
err = ErrNotCloak
return
}
2019-08-12 13:21:42 +00:00
if _, ok := sta.ProxyBook[info.ProxyMethod]; !ok {
2019-08-06 14:50:33 +00:00
err = ErrBadProxyMethod
return
}
finisher = func(sessionKey []byte) error {
reply, err := composeReply(ch, sharedSecret, sessionKey)
if err != nil {
return err
}
2019-08-06 14:50:33 +00:00
_, err = conn.Write(reply)
if err != nil {
go conn.Close()
return err
}
return nil
}
return
}