mirror of https://github.com/cbeuw/Cloak
Purge impurity
This commit is contained in:
parent
bbc0752883
commit
444182f5bb
|
|
@ -6,6 +6,7 @@ import (
|
|||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
localConfig, remoteConfig, authInfo, err := rawConfig.SplitConfigs()
|
||||
localConfig, remoteConfig, authInfo, err := rawConfig.SplitConfigs(common.RealWorldState)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,15 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/internal/server"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cbeuw/Cloak/internal/server"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var version string
|
||||
|
|
@ -145,7 +144,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
sta, err := server.InitState(raw, time.Now)
|
||||
sta, err := server.InitState(raw, common.RealWorldState)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to initialise server state: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net"
|
||||
)
|
||||
|
||||
const appDataMaxLength = 16401
|
||||
|
|
@ -67,7 +64,7 @@ type DirectTLS struct {
|
|||
// NewClientTransport handles the TLS handshake for a given conn and returns the sessionKey
|
||||
// if the server proceed with Cloak authentication
|
||||
func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo authInfo) (sessionKey [32]byte, err error) {
|
||||
payload, sharedSecret := makeAuthenticationPayload(authInfo, rand.Reader, time.Now())
|
||||
payload, sharedSecret := makeAuthenticationPayload(authInfo)
|
||||
chOnly := tls.browser.composeClientHello(genStegClientHello(payload, authInfo.MockDomain))
|
||||
chWithRecordLayer := common.AddRecordLayer(chOnly, common.Handshake, common.VersionTLS11)
|
||||
_, err = rawConn.Write(chWithRecordLayer)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import (
|
|||
"encoding/binary"
|
||||
"github.com/cbeuw/Cloak/internal/ecdh"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -19,7 +17,7 @@ type authenticationPayload struct {
|
|||
|
||||
// makeAuthenticationPayload generates the ephemeral key pair, calculates the shared secret, and then compose and
|
||||
// encrypt the authenticationPayload
|
||||
func makeAuthenticationPayload(authInfo authInfo, randReader io.Reader, time time.Time) (ret authenticationPayload, sharedSecret [32]byte) {
|
||||
func makeAuthenticationPayload(authInfo authInfo) (ret authenticationPayload, sharedSecret [32]byte) {
|
||||
/*
|
||||
Authentication data:
|
||||
+----------+----------------+---------------------+-------------+--------------+--------+------------+
|
||||
|
|
@ -28,14 +26,14 @@ func makeAuthenticationPayload(authInfo authInfo, randReader io.Reader, time tim
|
|||
| 16 bytes | 12 bytes | 1 byte | 8 bytes | 4 bytes | 1 byte | 6 bytes |
|
||||
+----------+----------------+---------------------+-------------+--------------+--------+------------+
|
||||
*/
|
||||
ephPv, ephPub, _ := ecdh.GenerateKey(randReader)
|
||||
ephPv, ephPub, _ := ecdh.GenerateKey(authInfo.WorldState.Rand)
|
||||
copy(ret.randPubKey[:], ecdh.Marshal(ephPub))
|
||||
|
||||
plaintext := make([]byte, 48)
|
||||
copy(plaintext, authInfo.UID)
|
||||
copy(plaintext[16:28], authInfo.ProxyMethod)
|
||||
plaintext[28] = authInfo.EncryptionMethod
|
||||
binary.BigEndian.PutUint64(plaintext[29:37], uint64(time.Unix()))
|
||||
binary.BigEndian.PutUint64(plaintext[29:37], uint64(authInfo.WorldState.Now().Unix()))
|
||||
binary.BigEndian.PutUint32(plaintext[37:41], authInfo.SessionId)
|
||||
|
||||
if authInfo.Unordered {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ func MakeSession(connConfig remoteConnConfig, authInfo authInfo, dialer common.D
|
|||
// sessionID is usergenerated. There shouldn't be a security concern because the scope of
|
||||
// sessionID is limited to its UID.
|
||||
quad := make([]byte, 4)
|
||||
util.CryptoRandRead(quad)
|
||||
util.RandRead(authInfo.WorldState.Rand, quad)
|
||||
authInfo.SessionId = binary.BigEndian.Uint32(quad)
|
||||
} else {
|
||||
authInfo.SessionId = 0
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
|
@ -13,11 +14,11 @@ import (
|
|||
mux "github.com/cbeuw/Cloak/internal/multiplex"
|
||||
)
|
||||
|
||||
// rawConfig represents the fields in the config json file
|
||||
// RawConfig represents the fields in the config json file
|
||||
// nullable means if it's empty, a default value will be chosen in SplitConfigs
|
||||
// jsonOptional means if the json's empty, its value will be set from environment variables or commandline args
|
||||
// but it mustn't be empty when SplitConfigs is called
|
||||
type rawConfig struct {
|
||||
type RawConfig struct {
|
||||
ServerName string
|
||||
ProxyMethod string
|
||||
EncryptionMethod string
|
||||
|
|
@ -57,6 +58,7 @@ type authInfo struct {
|
|||
Unordered bool
|
||||
ServerPubKey crypto.PublicKey
|
||||
MockDomain string
|
||||
WorldState common.WorldState
|
||||
}
|
||||
|
||||
// semi-colon separated value. This is for Android plugin options
|
||||
|
|
@ -98,7 +100,7 @@ func ssvToJson(ssv string) (ret []byte) {
|
|||
return ret
|
||||
}
|
||||
|
||||
func ParseConfig(conf string) (raw *rawConfig, err error) {
|
||||
func ParseConfig(conf string) (raw *RawConfig, err error) {
|
||||
var content []byte
|
||||
// Checking if it's a path to json or a ssv string
|
||||
if strings.Contains(conf, ";") && strings.Contains(conf, "=") {
|
||||
|
|
@ -110,7 +112,7 @@ func ParseConfig(conf string) (raw *rawConfig, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
raw = new(rawConfig)
|
||||
raw = new(RawConfig)
|
||||
err = json.Unmarshal(content, &raw)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -118,7 +120,7 @@ func ParseConfig(conf string) (raw *rawConfig, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (raw *rawConfig) SplitConfigs() (local localConnConfig, remote remoteConnConfig, auth authInfo, err error) {
|
||||
func (raw *RawConfig) SplitConfigs(worldState common.WorldState) (local localConnConfig, remote remoteConnConfig, auth authInfo, err error) {
|
||||
nullErr := func(field string) (local localConnConfig, remote remoteConnConfig, auth authInfo, err error) {
|
||||
err = fmt.Errorf("%v cannot be empty", field)
|
||||
return
|
||||
|
|
@ -148,6 +150,7 @@ func (raw *rawConfig) SplitConfigs() (local localConnConfig, remote remoteConnCo
|
|||
return
|
||||
}
|
||||
auth.ServerPubKey = pub
|
||||
auth.WorldState = worldState
|
||||
|
||||
// Encryption method
|
||||
switch strings.ToLower(raw.EncryptionMethod) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package client
|
||||
|
||||
import "net"
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type Transport interface {
|
||||
Handshake(rawConn net.Conn, authInfo authInfo) (sessionKey [32]byte, err error)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"github.com/gorilla/websocket"
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
type WSOverTLS struct {
|
||||
|
|
@ -37,7 +34,7 @@ func (ws *WSOverTLS) Handshake(rawConn net.Conn, authInfo authInfo) (sessionKey
|
|||
return sessionKey, fmt.Errorf("failed to parse ws url: %v", err)
|
||||
}
|
||||
|
||||
payload, sharedSecret := makeAuthenticationPayload(authInfo, rand.Reader, time.Now())
|
||||
payload, sharedSecret := makeAuthenticationPayload(authInfo)
|
||||
header := http.Header{}
|
||||
header.Add("hidden", base64.StdEncoding.EncodeToString(append(payload.randPubKey[:], payload.ciphertextWithTag[:]...)))
|
||||
c, _, err := websocket.NewClient(uconn, u, header, 16480, 16480)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
var RealWorldState = WorldState{
|
||||
Rand: rand.Reader,
|
||||
Now: time.Now,
|
||||
}
|
||||
|
||||
type WorldState struct {
|
||||
Rand io.Reader
|
||||
Now func() time.Time
|
||||
}
|
||||
|
|
@ -6,6 +6,9 @@ import (
|
|||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/internal/ecdh"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
|
@ -39,8 +42,24 @@ func (TLS) processFirstPacket(clientHello []byte, privateKey crypto.PrivateKey)
|
|||
}
|
||||
|
||||
func (TLS) makeResponder(clientHelloSessionId []byte, sharedSecret [32]byte) Responder {
|
||||
respond := func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error) {
|
||||
reply, err := composeReply(clientHelloSessionId, sharedSecret, sessionKey)
|
||||
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))])
|
||||
util.RandRead(randSource, cert)
|
||||
|
||||
var nonce [12]byte
|
||||
util.RandRead(randSource, nonce[:])
|
||||
encryptedSessionKey, err := util.AESGCMEncrypt(nonce[:], sharedSecret[:], sessionKey[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var encryptedSessionKeyArr [48]byte
|
||||
copy(encryptedSessionKeyArr[:], encryptedSessionKey)
|
||||
|
||||
reply, err := composeReply(clientHelloSessionId, nonce, encryptedSessionKeyArr, cert)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to compose TLS reply: %v", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// ClientHello contains every field in a ClientHello message
|
||||
|
|
@ -162,29 +161,21 @@ func parseClientHello(data []byte) (ret *ClientHello, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func composeServerHello(sessionId []byte, sharedSecret [32]byte, sessionKey [32]byte) ([]byte, error) {
|
||||
nonce := make([]byte, 12)
|
||||
util.CryptoRandRead(nonce)
|
||||
|
||||
encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret[:], sessionKey[:]) // 32 + 16 = 48 bytes
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func composeServerHello(sessionId []byte, nonce [12]byte, encryptedSessionKeyWithTag [48]byte) ([]byte, error) {
|
||||
var serverHello [11][]byte
|
||||
serverHello[0] = []byte{0x02} // handshake type
|
||||
serverHello[1] = []byte{0x00, 0x00, 0x76} // length 77
|
||||
serverHello[2] = []byte{0x03, 0x03} // server version
|
||||
serverHello[3] = append(nonce[0:12], encryptedKey[0:20]...) // random 32 bytes
|
||||
serverHello[4] = []byte{0x20} // session id length 32
|
||||
serverHello[5] = sessionId // session id
|
||||
serverHello[6] = []byte{0xc0, 0x30} // cipher suite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
serverHello[7] = []byte{0x00} // compression method null
|
||||
serverHello[8] = []byte{0x00, 0x2e} // extensions length 46
|
||||
serverHello[0] = []byte{0x02} // handshake type
|
||||
serverHello[1] = []byte{0x00, 0x00, 0x76} // length 77
|
||||
serverHello[2] = []byte{0x03, 0x03} // server version
|
||||
serverHello[3] = append(nonce[0:12], encryptedSessionKeyWithTag[0:20]...) // random 32 bytes
|
||||
serverHello[4] = []byte{0x20} // session id length 32
|
||||
serverHello[5] = sessionId // session id
|
||||
serverHello[6] = []byte{0xc0, 0x30} // cipher suite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
serverHello[7] = []byte{0x00} // compression method null
|
||||
serverHello[8] = []byte{0x00, 0x2e} // extensions length 46
|
||||
|
||||
keyShare, _ := hex.DecodeString("00330024001d0020")
|
||||
keyExchange := make([]byte, 32)
|
||||
copy(keyExchange, encryptedKey[20:48])
|
||||
copy(keyExchange, encryptedSessionKeyWithTag[20:48])
|
||||
util.CryptoRandRead(keyExchange[28:32])
|
||||
serverHello[9] = append(keyShare, keyExchange...)
|
||||
|
||||
|
|
@ -198,21 +189,15 @@ func composeServerHello(sessionId []byte, sharedSecret [32]byte, sessionKey [32]
|
|||
|
||||
// composeReply composes the ServerHello, ChangeCipherSpec and an ApplicationData messages
|
||||
// together with their respective record layers into one byte slice.
|
||||
func composeReply(clientHelloSessionId []byte, sharedSecret [32]byte, sessionKey [32]byte) ([]byte, error) {
|
||||
func composeReply(clientHelloSessionId []byte, nonce [12]byte, encryptedSessionKeyWithTag [48]byte, cert []byte) ([]byte, error) {
|
||||
TLS12 := []byte{0x03, 0x03}
|
||||
sh, err := composeServerHello(clientHelloSessionId, sharedSecret, sessionKey)
|
||||
sh, err := composeServerHello(clientHelloSessionId, nonce, encryptedSessionKeyWithTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shBytes := addRecordLayer(sh, []byte{0x16}, TLS12)
|
||||
ccsBytes := addRecordLayer([]byte{0x01}, []byte{0x14}, TLS12)
|
||||
|
||||
// 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))])
|
||||
util.CryptoRandRead(cert)
|
||||
encryptedCertBytes := addRecordLayer(cert, []byte{0x17}, TLS12)
|
||||
ret := append(shBytes, ccsBytes...)
|
||||
ret = append(ret, encryptedCertBytes...)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ var ErrTimestampOutOfWindow = errors.New("timestamp is outside of the accepting
|
|||
var ErrUnreconisedProtocol = errors.New("unreconised protocol")
|
||||
|
||||
// decryptClientInfo checks if a the authFragments are valid. It doesn't check if the UID is authorised
|
||||
func decryptClientInfo(fragments authFragments, now func() time.Time) (info ClientInfo, err error) {
|
||||
func decryptClientInfo(fragments authFragments, serverTime time.Time) (info ClientInfo, err error) {
|
||||
var plaintext []byte
|
||||
plaintext, err = util.AESGCMDecrypt(fragments.randPubKey[0:12], fragments.sharedSecret[:], fragments.ciphertextWithTag[:])
|
||||
if err != nil {
|
||||
|
|
@ -51,7 +51,6 @@ func decryptClientInfo(fragments authFragments, now func() time.Time) (info Clie
|
|||
|
||||
timestamp := int64(binary.BigEndian.Uint64(plaintext[29:37]))
|
||||
clientTime := time.Unix(timestamp, 0)
|
||||
serverTime := now()
|
||||
if !(clientTime.After(serverTime.Truncate(TIMESTAMP_TOLERANCE)) && clientTime.Before(serverTime.Add(TIMESTAMP_TOLERANCE))) {
|
||||
err = fmt.Errorf("%v: received timestamp %v", ErrTimestampOutOfWindow, timestamp)
|
||||
return
|
||||
|
|
@ -89,7 +88,7 @@ func AuthFirstPacket(firstPacket []byte, sta *State) (info ClientInfo, finisher
|
|||
return
|
||||
}
|
||||
|
||||
info, err = decryptClientInfo(fragments, sta.Now)
|
||||
info, err = decryptClientInfo(fragments, sta.WorldState.Now())
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
err = fmt.Errorf("transport %v in correct format but not Cloak: %v", transport, err)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func DispatchConnection(conn net.Conn, sta *State) {
|
|||
}
|
||||
|
||||
var sessionKey [32]byte
|
||||
util.CryptoRandRead(sessionKey[:])
|
||||
util.RandRead(sta.WorldState.Rand, sessionKey[:])
|
||||
obfuscator, err := mux.MakeObfuscator(ci.EncryptionMethod, sessionKey)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
|
@ -84,7 +84,7 @@ func DispatchConnection(conn net.Conn, sta *State) {
|
|||
// added to the userinfo database. The distinction between going into the admin mode
|
||||
// and normal proxy mode is that sessionID needs == 0 for admin mode
|
||||
if bytes.Equal(ci.UID, sta.AdminUID) && ci.SessionId == 0 {
|
||||
preparedConn, err := finishHandshake(conn, sessionKey)
|
||||
preparedConn, err := finishHandshake(conn, sessionKey, sta.WorldState.Rand)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
|
@ -125,7 +125,7 @@ func DispatchConnection(conn net.Conn, sta *State) {
|
|||
}
|
||||
|
||||
if existing {
|
||||
preparedConn, err := finishHandshake(conn, sesh.SessionKey)
|
||||
preparedConn, err := finishHandshake(conn, sesh.SessionKey, sta.WorldState.Rand)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
|
@ -135,7 +135,7 @@ func DispatchConnection(conn net.Conn, sta *State) {
|
|||
return
|
||||
}
|
||||
|
||||
preparedConn, err := finishHandshake(conn, sessionKey)
|
||||
preparedConn, err := finishHandshake(conn, sessionKey, sta.WorldState.Rand)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ type State struct {
|
|||
ProxyBook map[string]net.Addr
|
||||
ProxyDialer common.Dialer
|
||||
|
||||
Now func() time.Time
|
||||
AdminUID []byte
|
||||
Timeout time.Duration
|
||||
WorldState common.WorldState
|
||||
AdminUID []byte
|
||||
Timeout time.Duration
|
||||
//KeepAlive time.Duration
|
||||
|
||||
BypassUID map[[16]byte]struct{}
|
||||
|
|
@ -144,13 +144,13 @@ func ParseConfig(conf string) (raw RawConfig, err error) {
|
|||
}
|
||||
|
||||
// ParseConfig parses the config (either a path to json or the json itself as argument) into a State variable
|
||||
func InitState(preParse RawConfig, nowFunc func() time.Time) (sta *State, err error) {
|
||||
func InitState(preParse RawConfig, worldState common.WorldState) (sta *State, err error) {
|
||||
sta = &State{
|
||||
Now: nowFunc,
|
||||
BypassUID: make(map[[16]byte]struct{}),
|
||||
ProxyBook: map[string]net.Addr{},
|
||||
usedRandom: map[[32]byte]int64{},
|
||||
RedirDialer: &net.Dialer{},
|
||||
WorldState: worldState,
|
||||
}
|
||||
if preParse.CncMode {
|
||||
err = errors.New("command & control mode not implemented")
|
||||
|
|
@ -220,10 +220,10 @@ const CACHE_CLEAN_INTERVAL = 12 * time.Hour
|
|||
func (sta *State) UsedRandomCleaner() {
|
||||
for {
|
||||
time.Sleep(CACHE_CLEAN_INTERVAL)
|
||||
now := sta.Now()
|
||||
sta.usedRandomM.Lock()
|
||||
for key, t := range sta.usedRandom {
|
||||
if time.Unix(t, 0).Before(now.Add(TIMESTAMP_TOLERANCE)) {
|
||||
// todo: inpure time
|
||||
if time.Unix(t, 0).Before(sta.WorldState.Now().Add(TIMESTAMP_TOLERANCE)) {
|
||||
delete(sta.usedRandom, key)
|
||||
}
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ func (sta *State) UsedRandomCleaner() {
|
|||
func (sta *State) registerRandom(r [32]byte) bool {
|
||||
sta.usedRandomM.Lock()
|
||||
_, used := sta.usedRandom[r]
|
||||
sta.usedRandom[r] = sta.Now().Unix()
|
||||
sta.usedRandom[r] = sta.WorldState.Now().Unix()
|
||||
sta.usedRandomM.Unlock()
|
||||
return used
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ package server
|
|||
import (
|
||||
"crypto"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Responder = func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error)
|
||||
type Responder = func(originalConn net.Conn, sessionKey [32]byte, randSource io.Reader) (preparedConn net.Conn, err error)
|
||||
type Transport interface {
|
||||
processFirstPacket(reqPacket []byte, privateKey crypto.PrivateKey) (authFragments, Responder, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/ecdh"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
|
@ -39,7 +40,7 @@ func (WebSocket) processFirstPacket(reqPacket []byte, privateKey crypto.PrivateK
|
|||
}
|
||||
|
||||
func (WebSocket) makeResponder(reqPacket []byte, sharedSecret [32]byte) Responder {
|
||||
respond := func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error) {
|
||||
respond := func(originalConn net.Conn, sessionKey [32]byte, randSource io.Reader) (preparedConn net.Conn, err error) {
|
||||
handler := newWsHandshakeHandler()
|
||||
|
||||
// For an explanation of the following 3 lines, see the comments in websocketAux.go
|
||||
|
|
@ -48,7 +49,7 @@ func (WebSocket) makeResponder(reqPacket []byte, sharedSecret [32]byte) Responde
|
|||
<-handler.finished
|
||||
preparedConn = handler.conn
|
||||
nonce := make([]byte, 12)
|
||||
util.CryptoRandRead(nonce)
|
||||
util.RandRead(randSource, nonce)
|
||||
|
||||
// reply: [12 bytes nonce][32 bytes encrypted session key][16 bytes authentication tag]
|
||||
encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret[:], sessionKey[:]) // 32 + 16 = 48 bytes
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
|
@ -38,7 +39,11 @@ func AESGCMDecrypt(nonce []byte, key []byte, ciphertext []byte) ([]byte, error)
|
|||
}
|
||||
|
||||
func CryptoRandRead(buf []byte) {
|
||||
_, err := rand.Read(buf)
|
||||
RandRead(rand.Reader, buf)
|
||||
}
|
||||
|
||||
func RandRead(randSource io.Reader, buf []byte) {
|
||||
_, err := randSource.Read(buf)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue