2018-10-09 15:07:54 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2019-08-31 20:40:50 +00:00
|
|
|
"crypto"
|
2018-10-09 15:07:54 +00:00
|
|
|
"errors"
|
2019-08-02 00:01:19 +00:00
|
|
|
"fmt"
|
2020-04-08 23:39:40 +00:00
|
|
|
"github.com/cbeuw/Cloak/internal/common"
|
2019-08-31 20:40:50 +00:00
|
|
|
"github.com/cbeuw/Cloak/internal/ecdh"
|
2020-01-22 18:37:01 +00:00
|
|
|
"net"
|
2018-10-09 15:07:54 +00:00
|
|
|
|
2020-01-22 18:37:01 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
2019-08-02 00:01:19 +00:00
|
|
|
|
2020-04-09 15:37:46 +00:00
|
|
|
const appDataMaxLength = 16401
|
|
|
|
|
2020-01-22 18:37:01 +00:00
|
|
|
type TLS struct{}
|
2018-10-09 15:07:54 +00:00
|
|
|
|
2020-01-22 18:37:01 +00:00
|
|
|
var ErrBadClientHello = errors.New("non (or malformed) ClientHello")
|
2019-08-03 12:26:57 +00:00
|
|
|
|
2020-04-08 20:37:21 +00:00
|
|
|
func (TLS) String() string { return "TLS" }
|
2019-08-16 23:35:28 +00:00
|
|
|
|
2020-04-06 14:24:18 +00:00
|
|
|
func (TLS) processFirstPacket(clientHello []byte, privateKey crypto.PrivateKey) (fragments authFragments, respond Responder, err error) {
|
|
|
|
ch, err := parseClientHello(clientHello)
|
2020-01-22 18:37:01 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Debug(err)
|
|
|
|
err = ErrBadClientHello
|
|
|
|
return
|
2018-10-09 15:07:54 +00:00
|
|
|
}
|
2019-08-06 22:59:29 +00:00
|
|
|
|
2020-04-06 14:24:18 +00:00
|
|
|
fragments, err = TLS{}.unmarshalClientHello(ch, privateKey)
|
2019-08-06 22:59:29 +00:00
|
|
|
if err != nil {
|
2020-04-06 13:29:38 +00:00
|
|
|
err = fmt.Errorf("failed to unmarshal ClientHello into authFragments: %v", err)
|
2020-01-22 18:37:01 +00:00
|
|
|
return
|
2019-08-06 22:59:29 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 20:15:28 +00:00
|
|
|
respond = TLS{}.makeResponder(ch.sessionId, fragments.sharedSecret)
|
2020-04-06 14:24:18 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-07 20:15:28 +00:00
|
|
|
func (TLS) makeResponder(clientHelloSessionId []byte, sharedSecret [32]byte) Responder {
|
|
|
|
respond := func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error) {
|
2020-04-06 14:24:18 +00:00
|
|
|
reply, err := composeReply(clientHelloSessionId, sharedSecret, sessionKey)
|
2020-01-22 18:37:01 +00:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("failed to compose TLS reply: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2020-04-08 20:37:21 +00:00
|
|
|
_, err = originalConn.Write(reply)
|
2020-01-22 18:37:01 +00:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("failed to write TLS reply: %v", err)
|
2020-04-08 20:37:21 +00:00
|
|
|
go originalConn.Close()
|
2020-01-22 18:37:01 +00:00
|
|
|
return
|
|
|
|
}
|
2020-04-08 23:39:40 +00:00
|
|
|
preparedConn = &common.TLSConn{Conn: originalConn}
|
2020-01-22 18:37:01 +00:00
|
|
|
return
|
2018-10-09 15:07:54 +00:00
|
|
|
}
|
2020-04-06 14:24:18 +00:00
|
|
|
return respond
|
2018-10-09 15:07:54 +00:00
|
|
|
}
|
2019-08-06 14:50:33 +00:00
|
|
|
|
2020-04-06 14:24:18 +00:00
|
|
|
func (TLS) unmarshalClientHello(ch *ClientHello, staticPv crypto.PrivateKey) (fragments authFragments, err error) {
|
2020-04-06 13:29:38 +00:00
|
|
|
copy(fragments.randPubKey[:], ch.random)
|
|
|
|
ephPub, ok := ecdh.Unmarshal(fragments.randPubKey[:])
|
2019-08-31 20:40:50 +00:00
|
|
|
if !ok {
|
|
|
|
err = ErrInvalidPubKey
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-06 13:29:38 +00:00
|
|
|
copy(fragments.sharedSecret[:], ecdh.GenerateSharedSecret(staticPv, ephPub))
|
2019-08-31 20:40:50 +00:00
|
|
|
var keyShare []byte
|
|
|
|
keyShare, err = parseKeyShare(ch.extensions[[2]byte{0x00, 0x33}])
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-01-24 15:13:26 +00:00
|
|
|
ctxTag := append(ch.sessionId, keyShare...)
|
|
|
|
if len(ctxTag) != 64 {
|
|
|
|
err = fmt.Errorf("%v: %v", ErrCiphertextLength, len(ctxTag))
|
2019-08-31 20:40:50 +00:00
|
|
|
return
|
|
|
|
}
|
2020-04-06 13:29:38 +00:00
|
|
|
copy(fragments.ciphertextWithTag[:], ctxTag)
|
2019-08-31 20:40:50 +00:00
|
|
|
return
|
|
|
|
}
|