2019-09-01 00:33:34 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2020-01-22 18:37:01 +00:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2019-09-01 00:33:34 +00:00
|
|
|
"crypto"
|
2020-01-22 18:37:01 +00:00
|
|
|
"encoding/base64"
|
2019-09-01 00:33:34 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/cbeuw/Cloak/internal/ecdh"
|
|
|
|
"github.com/cbeuw/Cloak/internal/util"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
)
|
|
|
|
|
2020-01-22 18:37:01 +00:00
|
|
|
type WebSocket struct{}
|
2019-09-01 00:33:34 +00:00
|
|
|
|
2020-01-22 18:37:01 +00:00
|
|
|
func (WebSocket) String() string { return "WebSocket" }
|
|
|
|
func (WebSocket) HasRecordLayer() bool { return false }
|
|
|
|
func (WebSocket) UnitReadFunc() func(net.Conn, []byte) (int, error) { return util.ReadWebSocket }
|
2019-09-01 00:33:34 +00:00
|
|
|
|
2020-04-06 13:29:38 +00:00
|
|
|
func (WebSocket) handshake(reqPacket []byte, privateKey crypto.PrivateKey, originalConn net.Conn) (fragments authFragments, finisher func([]byte) (net.Conn, error), err error) {
|
2020-01-22 18:37:01 +00:00
|
|
|
var req *http.Request
|
|
|
|
req, err = http.ReadRequest(bufio.NewReader(bytes.NewBuffer(reqPacket)))
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("failed to parse first HTTP GET: %v", err)
|
|
|
|
return
|
2019-09-01 00:33:34 +00:00
|
|
|
}
|
2020-01-22 18:37:01 +00:00
|
|
|
var hiddenData []byte
|
|
|
|
hiddenData, err = base64.StdEncoding.DecodeString(req.Header.Get("hidden"))
|
2019-09-01 00:33:34 +00:00
|
|
|
|
2020-04-06 13:29:38 +00:00
|
|
|
fragments, err = unmarshalHidden(hiddenData, privateKey)
|
2020-01-22 18:37:01 +00:00
|
|
|
if err != nil {
|
2020-04-06 13:29:38 +00:00
|
|
|
err = fmt.Errorf("failed to unmarshal hidden data from WS into authFragments: %v", err)
|
2020-01-22 18:37:01 +00:00
|
|
|
return
|
2019-09-01 00:33:34 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 18:37:01 +00:00
|
|
|
finisher = func(sessionKey []byte) (preparedConn net.Conn, err error) {
|
|
|
|
handler := newWsHandshakeHandler()
|
|
|
|
|
|
|
|
// For an explanation of the following 3 lines, see the comments in websocketAux.go
|
|
|
|
http.Serve(newWsAcceptor(originalConn, reqPacket), handler)
|
|
|
|
|
|
|
|
<-handler.finished
|
|
|
|
preparedConn = handler.conn
|
|
|
|
nonce := make([]byte, 12)
|
2020-02-01 23:46:46 +00:00
|
|
|
util.CryptoRandRead(nonce)
|
2020-01-22 18:37:01 +00:00
|
|
|
|
|
|
|
// reply: [12 bytes nonce][32 bytes encrypted session key][16 bytes authentication tag]
|
2020-04-06 13:29:38 +00:00
|
|
|
encryptedKey, err := util.AESGCMEncrypt(nonce, fragments.sharedSecret[:], sessionKey) // 32 + 16 = 48 bytes
|
2020-01-22 18:37:01 +00:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("failed to encrypt reply: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
reply := append(nonce, encryptedKey...)
|
|
|
|
_, err = preparedConn.Write(reply)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("failed to write reply: %v", err)
|
|
|
|
go preparedConn.Close()
|
|
|
|
return
|
|
|
|
}
|
2019-09-01 00:33:34 +00:00
|
|
|
return
|
|
|
|
}
|
2020-01-22 18:37:01 +00:00
|
|
|
|
|
|
|
return
|
2019-09-01 00:33:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var ErrBadGET = errors.New("non (or malformed) HTTP GET")
|
|
|
|
|
2020-04-06 13:29:38 +00:00
|
|
|
func unmarshalHidden(hidden []byte, staticPv crypto.PrivateKey) (fragments authFragments, err error) {
|
2019-09-01 00:33:34 +00:00
|
|
|
if len(hidden) < 96 {
|
|
|
|
err = ErrBadGET
|
|
|
|
return
|
|
|
|
}
|
2020-01-24 14:38:41 +00:00
|
|
|
|
2020-04-06 13:29:38 +00:00
|
|
|
copy(fragments.randPubKey[:], hidden[0:32])
|
|
|
|
ephPub, ok := ecdh.Unmarshal(fragments.randPubKey[:])
|
2019-09-01 00:33:34 +00:00
|
|
|
if !ok {
|
|
|
|
err = ErrInvalidPubKey
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-06 13:29:38 +00:00
|
|
|
copy(fragments.sharedSecret[:], ecdh.GenerateSharedSecret(staticPv, ephPub))
|
2019-09-01 00:33:34 +00:00
|
|
|
|
2020-01-24 15:13:26 +00:00
|
|
|
if len(hidden[32:]) != 64 {
|
|
|
|
err = fmt.Errorf("%v: %v", ErrCiphertextLength, len(hidden[32:]))
|
2019-09-01 00:33:34 +00:00
|
|
|
return
|
|
|
|
}
|
2020-01-24 15:13:26 +00:00
|
|
|
|
2020-04-06 13:29:38 +00:00
|
|
|
copy(fragments.ciphertextWithTag[:], hidden[32:])
|
2019-09-01 00:33:34 +00:00
|
|
|
return
|
|
|
|
}
|