2019-08-02 15:02:25 +00:00
|
|
|
package client
|
2018-10-07 17:09:45 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
2020-04-08 23:39:40 +00:00
|
|
|
"github.com/cbeuw/Cloak/internal/common"
|
2019-08-06 14:50:33 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2020-04-09 21:11:12 +00:00
|
|
|
"net"
|
2018-10-07 17:09:45 +00:00
|
|
|
)
|
|
|
|
|
2020-04-09 15:37:46 +00:00
|
|
|
const appDataMaxLength = 16401
|
|
|
|
|
2020-01-24 16:44:29 +00:00
|
|
|
type clientHelloFields struct {
|
|
|
|
random []byte
|
|
|
|
sessionId []byte
|
|
|
|
x25519KeyShare []byte
|
|
|
|
sni []byte
|
|
|
|
}
|
|
|
|
|
2019-08-16 23:18:19 +00:00
|
|
|
type browser interface {
|
2020-01-24 16:44:29 +00:00
|
|
|
composeClientHello(clientHelloFields) []byte
|
2018-10-07 17:09:45 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 00:01:19 +00:00
|
|
|
func makeServerName(serverName string) []byte {
|
2018-10-07 17:09:45 +00:00
|
|
|
serverNameListLength := make([]byte, 2)
|
|
|
|
binary.BigEndian.PutUint16(serverNameListLength, uint16(len(serverName)+3))
|
|
|
|
serverNameType := []byte{0x00} // host_name
|
|
|
|
serverNameLength := make([]byte, 2)
|
|
|
|
binary.BigEndian.PutUint16(serverNameLength, uint16(len(serverName)))
|
|
|
|
ret := make([]byte, 2+1+2+len(serverName))
|
|
|
|
copy(ret[0:2], serverNameListLength)
|
|
|
|
copy(ret[2:3], serverNameType)
|
|
|
|
copy(ret[3:5], serverNameLength)
|
|
|
|
copy(ret[5:], serverName)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// addExtensionRecord, add type, length to extension data
|
|
|
|
func addExtRec(typ []byte, data []byte) []byte {
|
|
|
|
length := make([]byte, 2)
|
|
|
|
binary.BigEndian.PutUint16(length, uint16(len(data)))
|
|
|
|
ret := make([]byte, 2+2+len(data))
|
|
|
|
copy(ret[0:2], typ)
|
|
|
|
copy(ret[2:4], length)
|
|
|
|
copy(ret[4:], data)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2020-04-06 12:07:16 +00:00
|
|
|
func genStegClientHello(ai authenticationPayload, serverName string) (ret clientHelloFields) {
|
2020-01-25 10:19:45 +00:00
|
|
|
// random is marshalled ephemeral pub key 32 bytes
|
|
|
|
// The authentication ciphertext and its tag are then distributed among SessionId and X25519KeyShare
|
2020-01-24 16:44:29 +00:00
|
|
|
ret.random = ai.randPubKey[:]
|
|
|
|
ret.sessionId = ai.ciphertextWithTag[0:32]
|
|
|
|
ret.x25519KeyShare = ai.ciphertextWithTag[32:64]
|
|
|
|
ret.sni = makeServerName(serverName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-09-02 13:03:10 +00:00
|
|
|
type DirectTLS struct {
|
2020-04-08 23:39:40 +00:00
|
|
|
*common.TLSConn
|
2020-04-08 19:53:09 +00:00
|
|
|
browser browser
|
2019-08-31 17:01:39 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 19:53:09 +00:00
|
|
|
// NewClientTransport handles the TLS handshake for a given conn and returns the sessionKey
|
2019-08-20 21:43:04 +00:00
|
|
|
// if the server proceed with Cloak authentication
|
2020-04-10 13:09:48 +00:00
|
|
|
func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey [32]byte, err error) {
|
2020-04-09 21:11:12 +00:00
|
|
|
payload, sharedSecret := makeAuthenticationPayload(authInfo)
|
2020-04-06 12:07:16 +00:00
|
|
|
chOnly := tls.browser.composeClientHello(genStegClientHello(payload, authInfo.MockDomain))
|
2020-04-08 23:39:40 +00:00
|
|
|
chWithRecordLayer := common.AddRecordLayer(chOnly, common.Handshake, common.VersionTLS11)
|
2020-04-08 19:53:09 +00:00
|
|
|
_, err = rawConn.Write(chWithRecordLayer)
|
2019-08-06 14:50:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.Trace("client hello sent successfully")
|
2020-04-08 23:39:40 +00:00
|
|
|
tls.TLSConn = &common.TLSConn{Conn: rawConn}
|
2019-08-06 14:50:33 +00:00
|
|
|
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
log.Trace("waiting for ServerHello")
|
2020-04-08 19:53:09 +00:00
|
|
|
_, err = tls.Read(buf)
|
2019-08-06 14:50:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-08-06 22:59:29 +00:00
|
|
|
|
2020-04-08 19:53:09 +00:00
|
|
|
encrypted := append(buf[6:38], buf[84:116]...)
|
2019-08-06 22:59:29 +00:00
|
|
|
nonce := encrypted[0:12]
|
2019-08-20 21:43:04 +00:00
|
|
|
ciphertextWithTag := encrypted[12:60]
|
2020-04-14 00:53:28 +00:00
|
|
|
sessionKeySlice, err := common.AESGCMDecrypt(nonce, sharedSecret[:], ciphertextWithTag)
|
2019-08-06 22:59:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2020-04-07 20:15:28 +00:00
|
|
|
copy(sessionKey[:], sessionKeySlice)
|
2019-08-06 22:59:29 +00:00
|
|
|
|
2019-08-06 23:28:08 +00:00
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
// ChangeCipherSpec and EncryptedCert (in the format of application data)
|
2020-04-08 19:53:09 +00:00
|
|
|
_, err = tls.Read(buf)
|
2019-08-06 23:28:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-08-06 14:50:33 +00:00
|
|
|
}
|
2020-04-08 19:53:09 +00:00
|
|
|
return sessionKey, nil
|
2019-08-06 14:50:33 +00:00
|
|
|
|
|
|
|
}
|