You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Cloak/internal/server/websocket.go

93 lines
2.5 KiB
Go

5 years ago
package server
import (
"bufio"
"bytes"
5 years ago
"crypto"
"encoding/base64"
5 years ago
"errors"
"fmt"
"github.com/cbeuw/Cloak/internal/ecdh"
"github.com/cbeuw/Cloak/internal/util"
"net"
"net/http"
)
type WebSocket struct{}
5 years ago
func (WebSocket) String() string { return "WebSocket" }
func (WebSocket) HasRecordLayer() bool { return false }
func (WebSocket) UnitReadFunc() func(net.Conn, []byte) (int, error) { return util.ReadWebSocket }
5 years ago
func (WebSocket) handshake(reqPacket []byte, privateKey crypto.PrivateKey, originalConn net.Conn) (ai authenticationInfo, finisher func([]byte) (net.Conn, error), err error) {
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
5 years ago
}
var hiddenData []byte
hiddenData, err = base64.StdEncoding.DecodeString(req.Header.Get("hidden"))
5 years ago
ai, err = unmarshalHidden(hiddenData, privateKey)
if err != nil {
err = fmt.Errorf("failed to unmarshal hidden data from WS into authenticationInfo: %v", err)
return
5 years ago
}
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)
util.CryptoRandRead(nonce)
// reply: [12 bytes nonce][32 bytes encrypted session key][16 bytes authentication tag]
encryptedKey, err := util.AESGCMEncrypt(nonce, ai.sharedSecret[:], sessionKey) // 32 + 16 = 48 bytes
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
}
5 years ago
return
}
return
5 years ago
}
var ErrBadGET = errors.New("non (or malformed) HTTP GET")
func unmarshalHidden(hidden []byte, staticPv crypto.PrivateKey) (ai authenticationInfo, err error) {
if len(hidden) < 96 {
err = ErrBadGET
return
}
copy(ai.randPubKey[:], hidden[0:32])
ephPub, ok := ecdh.Unmarshal(ai.randPubKey[:])
5 years ago
if !ok {
err = ErrInvalidPubKey
return
}
copy(ai.sharedSecret[:], ecdh.GenerateSharedSecret(staticPv, ephPub))
5 years ago
if len(hidden[32:]) != 64 {
err = fmt.Errorf("%v: %v", ErrCiphertextLength, len(hidden[32:]))
5 years ago
return
}
copy(ai.ciphertextWithTag[:], hidden[32:])
5 years ago
return
}