Cloak/internal/server/TLS.go

97 lines
2.6 KiB
Go
Raw Normal View History

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-04-09 21:11:12 +00:00
"io"
"math/rand"
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
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" }
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
}
2020-04-06 14:24:18 +00:00
fragments, err = TLS{}.unmarshalClientHello(ch, privateKey)
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
}
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 {
2020-04-09 21:11:12 +00:00
respond := func(originalConn net.Conn, sessionKey [32]byte, randSource io.Reader) (preparedConn net.Conn, err error) {
// the cert length needs to be the same for all handshakes belonging to the same session
// we can use sessionKey as a seed here to ensure consistency
possibleCertLengths := []int{42, 27, 68, 59, 36, 44, 46}
rand.Seed(int64(sessionKey[0]))
cert := make([]byte, possibleCertLengths[rand.Intn(len(possibleCertLengths))])
2020-04-14 00:53:28 +00:00
common.RandRead(randSource, cert)
2020-04-09 21:11:12 +00:00
var nonce [12]byte
2020-04-14 00:53:28 +00:00
common.RandRead(randSource, nonce[:])
encryptedSessionKey, err := common.AESGCMEncrypt(nonce[:], sharedSecret[:], sessionKey[:])
2020-04-09 21:11:12 +00:00
if err != nil {
return
}
var encryptedSessionKeyArr [48]byte
copy(encryptedSessionKeyArr[:], encryptedSessionKey)
2020-04-10 13:11:01 +00:00
reply := composeReply(clientHelloSessionId, nonce, encryptedSessionKeyArr, cert)
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)
originalConn.Close()
2020-01-22 18:37:01 +00:00
return
}
preparedConn = common.NewTLSConn(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
}