mirror of https://github.com/cbeuw/Cloak
Use ECDH instead of ECIES
This commit is contained in:
parent
b9f2aa4ed0
commit
a8786a5576
|
|
@ -112,13 +112,7 @@ func main() {
|
|||
log.Printf("Starting standalone mode. Listening for ss on %v:%v\n", localHost, localPort)
|
||||
}
|
||||
|
||||
sta := &client.State{
|
||||
SS_LOCAL_HOST: localHost,
|
||||
SS_LOCAL_PORT: localPort,
|
||||
SS_REMOTE_HOST: remoteHost,
|
||||
SS_REMOTE_PORT: remotePort,
|
||||
Now: time.Now,
|
||||
}
|
||||
sta := client.InitState(localHost, localPort, remoteHost, remotePort, time.Now)
|
||||
err := sta.ParseConfig(pluginOpts)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ var version string
|
|||
|
||||
func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
|
||||
for {
|
||||
i, err := io.Copy(dst, src)
|
||||
if err != nil || i == 0 {
|
||||
_, err := io.Copy(dst, src)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
go dst.Close()
|
||||
go src.Close()
|
||||
return
|
||||
|
|
@ -159,15 +160,7 @@ func main() {
|
|||
localPort = strings.Split(*localAddr, ":")[1]
|
||||
log.Printf("Starting standalone mode, listening on %v:%v to ss at %v:%v\n", remoteHost, remotePort, localHost, localPort)
|
||||
}
|
||||
sta := &server.State{
|
||||
SS_LOCAL_HOST: localHost,
|
||||
SS_LOCAL_PORT: localPort,
|
||||
SS_REMOTE_HOST: remoteHost,
|
||||
SS_REMOTE_PORT: remotePort,
|
||||
Now: time.Now,
|
||||
UsedRandom: map[[32]byte]int{},
|
||||
Sessions: map[[32]byte]*mux.Session{},
|
||||
}
|
||||
sta := server.InitState(localHost, localPort, remoteHost, remotePort, time.Now)
|
||||
err := sta.ParseConfig(pluginOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Configuration file error: %v", err)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"ServerName":"www.bing.com",
|
||||
"Key":"UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFwEOF8c8XRX8iYVdjKW2BAfym2zppExMPteovDB/Q8phdD53FnH39tQ1daaVLn9+FIGOAdk+UZZ2aOt5jSK638YPg==",
|
||||
"Key":"UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFy2wjljjjqJWGiNoAYpWscJ0VEVkewo6o8S/jcNdNxFLQ==",
|
||||
"TicketTimeHint":3600,
|
||||
"NumConn":4,
|
||||
"MaskBrowser":"chrome"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"WebServerAddr":"204.79.197.200:443",
|
||||
"Key":"H2pMM834RzkouOoRGNhbiQRnm4Ggy8sg+S6ve5yYfqUEOF8c8XRX8iYVdjKW2BAfym2zppExMPteovDB/Q8phdD53FnH39tQ1daaVLn9+FIGOAdk+UZZ2aOt5jSK638YPg=="
|
||||
"Key":"CN+VRP9OqZR0+Im2X/1y6FvaK7+GBnX6qCiovbo+eVo="
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,57 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"github.com/cbeuw/ecies"
|
||||
ecdh "github.com/cbeuw/go-ecdh"
|
||||
"io"
|
||||
)
|
||||
|
||||
func MakeRandomField(sta *State) []byte {
|
||||
type keyPair struct {
|
||||
crypto.PrivateKey
|
||||
crypto.PublicKey
|
||||
}
|
||||
|
||||
func MakeRandomField(sta *State) []byte {
|
||||
t := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(t, uint64(sta.Now().Unix()/12*60*60))
|
||||
rand := util.PsudoRandBytes(16, sta.Now().UnixNano())
|
||||
binary.BigEndian.PutUint64(t, uint64(sta.Now().Unix()/(12*60*60)))
|
||||
rdm := make([]byte, 16)
|
||||
io.ReadFull(rand.Reader, rdm)
|
||||
preHash := make([]byte, 56)
|
||||
copy(preHash[0:32], sta.SID)
|
||||
copy(preHash[32:40], t)
|
||||
copy(preHash[40:56], rand)
|
||||
copy(preHash[40:56], rdm)
|
||||
h := sha256.New()
|
||||
h.Write(preHash)
|
||||
ret := make([]byte, 32)
|
||||
copy(ret[0:16], rand)
|
||||
copy(ret[0:16], rdm)
|
||||
copy(ret[16:32], h.Sum(nil)[0:16])
|
||||
return ret
|
||||
}
|
||||
|
||||
func MakeSessionTicket(sta *State) []byte {
|
||||
t := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(t, uint64(sta.Now().Unix()/int64(sta.TicketTimeHint)))
|
||||
plain := make([]byte, 40)
|
||||
copy(plain, sta.SID)
|
||||
copy(plain[32:], t)
|
||||
// With the default settings (P256, AES128, SHA256) of the ecies package, len(ct)==153.
|
||||
//
|
||||
// ciphertext is composed of 3 parts: marshalled X and Y coordinates on the curve,
|
||||
// iv+ciphertext of the block cipher (aes128 in this case),
|
||||
// and the hmac which is 32 bytes because it's sha256
|
||||
//
|
||||
// The marshalling is done by crypto/elliptic.Marshal. According to the code,
|
||||
// the size after marshall is 65
|
||||
//
|
||||
// IV is 16 bytes. The size of ciphertext is equal to the plaintext, which is 40,
|
||||
// that is 32 bytes of SID + 8 bytes of timestamp/tickettimehint.
|
||||
// 16+40 = 56
|
||||
//
|
||||
// Then the hmac is 32 bytes
|
||||
//
|
||||
// 65+56+32=153
|
||||
ct, _ := ecies.Encrypt(rand.Reader, sta.pub, plain, nil, nil)
|
||||
sessionTicket := make([]byte, 192)
|
||||
// The reason for ct[1:] is that, the first byte of ct is always 0x04
|
||||
// This is specified in the section 4.3.6 of ANSI X9.62 (the uncompressed form).
|
||||
// This is a flag that is useless to us and it will expose our pattern
|
||||
// (because the sessionTicket isn't fully random anymore). Therefore we drop it.
|
||||
copy(sessionTicket, ct[1:])
|
||||
copy(sessionTicket[152:], util.PsudoRandBytes(40, sta.Now().UnixNano()))
|
||||
return sessionTicket
|
||||
// sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted SID 32 bytes][padding 128 bytes]
|
||||
// The first 16 bytes of the marshalled ephemeral public key is used as the IV
|
||||
// for encrypting the SID
|
||||
tthInterval := sta.Now().Unix() / int64(sta.TicketTimeHint)
|
||||
ec := ecdh.NewCurve25519ECDH()
|
||||
ephKP := sta.getKeyPair(tthInterval)
|
||||
if ephKP == nil {
|
||||
ephPv, ephPub, _ := ec.GenerateKey(rand.Reader)
|
||||
ephKP = &keyPair{
|
||||
ephPv,
|
||||
ephPub,
|
||||
}
|
||||
sta.putKeyPair(tthInterval, ephKP)
|
||||
}
|
||||
ticket := make([]byte, 192)
|
||||
copy(ticket[0:32], ec.Marshal(ephKP.PublicKey))
|
||||
key, _ := ec.GenerateSharedSecret(ephKP.PrivateKey, sta.staticPub)
|
||||
cipherSID := util.AESEncrypt(ticket[0:16], key, sta.SID)
|
||||
copy(ticket[32:64], cipherSID)
|
||||
io.ReadFull(rand.Reader, ticket[64:192])
|
||||
return ticket
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/cbeuw/ecies"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
ecdh "github.com/cbeuw/go-ecdh"
|
||||
)
|
||||
|
||||
type rawConfig struct {
|
||||
|
|
@ -25,15 +27,31 @@ type State struct {
|
|||
SS_LOCAL_PORT string
|
||||
SS_REMOTE_HOST string
|
||||
SS_REMOTE_PORT string
|
||||
|
||||
Now func() time.Time
|
||||
SID []byte
|
||||
pub *ecies.PublicKey
|
||||
staticPub crypto.PublicKey
|
||||
keyPairsM sync.RWMutex
|
||||
keyPairs map[int64]*keyPair
|
||||
|
||||
TicketTimeHint int
|
||||
ServerName string
|
||||
MaskBrowser string
|
||||
NumConn int
|
||||
}
|
||||
|
||||
func InitState(localHost, localPort, remoteHost, remotePort string, nowFunc func() time.Time) *State {
|
||||
ret := &State{
|
||||
SS_LOCAL_HOST: localHost,
|
||||
SS_LOCAL_PORT: localPort,
|
||||
SS_REMOTE_HOST: remoteHost,
|
||||
SS_REMOTE_PORT: remotePort,
|
||||
Now: nowFunc,
|
||||
}
|
||||
ret.keyPairs = make(map[int64]*keyPair)
|
||||
return ret
|
||||
}
|
||||
|
||||
// semi-colon separated value. This is for Android plugin options
|
||||
func ssvToJson(ssv string) (ret []byte) {
|
||||
unescape := func(s string) string {
|
||||
|
|
@ -89,24 +107,29 @@ func (sta *State) ParseConfig(conf string) (err error) {
|
|||
return errors.New("Failed to parse Key: " + err.Error())
|
||||
}
|
||||
sta.SID = sid
|
||||
sta.pub = pub
|
||||
sta.staticPub = pub
|
||||
return nil
|
||||
}
|
||||
|
||||
// Structure: [SID 32 bytes][marshalled public key]
|
||||
func parseKey(b64 string) ([]byte, *ecies.PublicKey, error) {
|
||||
// Structure: [SID 32 bytes][marshalled public key 32 bytes]
|
||||
func parseKey(b64 string) ([]byte, crypto.PublicKey, error) {
|
||||
b, err := base64.StdEncoding.DecodeString(b64)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sid := b[0:32]
|
||||
marshalled := b[32:]
|
||||
x, y := elliptic.Unmarshal(ecies.DefaultCurve, marshalled)
|
||||
pub := &ecies.PublicKey{
|
||||
X: x,
|
||||
Y: y,
|
||||
Curve: ecies.DefaultCurve,
|
||||
Params: ecies.ParamsFromCurve(ecies.DefaultCurve),
|
||||
ec := ecdh.NewCurve25519ECDH()
|
||||
pub, _ := ec.Unmarshal(b[32:64])
|
||||
return b[0:32], pub, nil
|
||||
}
|
||||
return sid, pub, nil
|
||||
|
||||
func (sta *State) getKeyPair(tthInterval int64) *keyPair {
|
||||
sta.keyPairsM.Lock()
|
||||
defer sta.keyPairsM.Unlock()
|
||||
return sta.keyPairs[tthInterval]
|
||||
}
|
||||
|
||||
func (sta *State) putKeyPair(tthInterval int64, kp *keyPair) {
|
||||
sta.keyPairsM.Lock()
|
||||
sta.keyPairs[tthInterval] = kp
|
||||
sta.keyPairsM.Unlock()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package multiplex
|
|||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
|
@ -70,6 +71,7 @@ func (stream *Stream) Read(buf []byte) (n int, err error) {
|
|||
func (stream *Stream) Write(in []byte) (n int, err error) {
|
||||
select {
|
||||
case <-stream.die:
|
||||
log.Printf("Stream %v dying\n", stream.id)
|
||||
return 0, errors.New(errBrokenPipe)
|
||||
default:
|
||||
}
|
||||
|
|
@ -100,6 +102,8 @@ func (stream *Stream) Write(in []byte) (n int, err error) {
|
|||
}
|
||||
|
||||
func (stream *Stream) Close() error {
|
||||
log.Printf("ID: %v closing\n", stream.id)
|
||||
|
||||
// Because closing a closed channel causes panic
|
||||
stream.closingM.Lock()
|
||||
defer stream.closingM.Unlock()
|
||||
|
|
|
|||
|
|
@ -79,7 +79,10 @@ type sentNotifier struct {
|
|||
|
||||
func (ce *connEnclave) send(data []byte) {
|
||||
// TODO: error handling
|
||||
n, _ := ce.remoteConn.Write(data)
|
||||
n, err := ce.remoteConn.Write(data)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
sn := &sentNotifier{
|
||||
ce,
|
||||
n,
|
||||
|
|
@ -121,7 +124,7 @@ func (sb *switchboard) dispatch() {
|
|||
}
|
||||
|
||||
func (sb *switchboard) deplex(ce *connEnclave) {
|
||||
buf := make([]byte, 20480)
|
||||
buf := make([]byte, 204800)
|
||||
for {
|
||||
i, err := sb.session.obfsedReader(ce.remoteConn, buf)
|
||||
if err != nil {
|
||||
|
|
@ -136,6 +139,7 @@ func (sb *switchboard) deplex(ce *connEnclave) {
|
|||
stream = sb.session.addStream(frame.StreamID)
|
||||
}
|
||||
if closing := sb.session.getStream(frame.ClosingStreamID); closing != nil {
|
||||
log.Printf("HeaderClosing: %v\n", frame.ClosingStreamID)
|
||||
closing.Close()
|
||||
}
|
||||
stream.newFrameCh <- frame
|
||||
|
|
|
|||
|
|
@ -2,32 +2,35 @@ package server
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"github.com/cbeuw/ecies"
|
||||
"log"
|
||||
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
ecdh "github.com/cbeuw/go-ecdh"
|
||||
)
|
||||
|
||||
// input ticket, return SID
|
||||
func decryptSessionTicket(pv *ecies.PrivateKey, ticket []byte) ([]byte, error) {
|
||||
ciphertext := make([]byte, 153)
|
||||
ciphertext[0] = 0x04
|
||||
copy(ciphertext[1:], ticket)
|
||||
plaintext, err := pv.Decrypt(ciphertext, nil, nil)
|
||||
func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) ([]byte, error) {
|
||||
ec := ecdh.NewCurve25519ECDH()
|
||||
ephPub, _ := ec.Unmarshal(ticket[0:32])
|
||||
key, err := ec.GenerateSharedSecret(staticPv, ephPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext[0:32], nil
|
||||
SID := util.AESDecrypt(ticket[0:16], key, ticket[32:64])
|
||||
return SID, nil
|
||||
}
|
||||
|
||||
func validateRandom(random []byte, SID []byte, time int64) bool {
|
||||
t := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(t, uint64(time/12*60*60))
|
||||
rand := random[0:16]
|
||||
binary.BigEndian.PutUint64(t, uint64(time/(12*60*60)))
|
||||
rdm := random[0:16]
|
||||
preHash := make([]byte, 56)
|
||||
copy(preHash[0:32], SID)
|
||||
copy(preHash[32:40], t)
|
||||
copy(preHash[40:56], rand)
|
||||
copy(preHash[40:56], rdm)
|
||||
h := sha256.New()
|
||||
h.Write(preHash)
|
||||
return bytes.Equal(h.Sum(nil)[0:16], random[16:32])
|
||||
|
|
@ -42,10 +45,12 @@ func TouchStone(ch *ClientHello, sta *State) (bool, []byte) {
|
|||
}
|
||||
sta.putUsedRandom(random)
|
||||
|
||||
SID, err := decryptSessionTicket(sta.pv, ch.extensions[[2]byte{0x00, 0x23}])
|
||||
SID, err := decryptSessionTicket(sta.staticPv, ch.extensions[[2]byte{0x00, 0x23}])
|
||||
if err != nil {
|
||||
log.Printf("ts: %v\n", err)
|
||||
return false, nil
|
||||
}
|
||||
log.Printf("SID: %x\n", SID)
|
||||
isSS := validateRandom(ch.random, SID, sta.Now().Unix())
|
||||
if !isSS {
|
||||
return false, nil
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
mux "github.com/cbeuw/Cloak/internal/multiplex"
|
||||
"github.com/cbeuw/ecies"
|
||||
)
|
||||
|
||||
type rawConfig struct {
|
||||
|
|
@ -26,18 +24,32 @@ type stateManager interface {
|
|||
|
||||
// State type stores the global state of the program
|
||||
type State struct {
|
||||
WebServerAddr string
|
||||
Now func() time.Time
|
||||
SS_LOCAL_HOST string
|
||||
SS_LOCAL_PORT string
|
||||
SS_REMOTE_HOST string
|
||||
SS_REMOTE_PORT string
|
||||
UsedRandomM sync.RWMutex
|
||||
UsedRandom map[[32]byte]int
|
||||
pv *ecies.PrivateKey
|
||||
|
||||
SessionsM sync.RWMutex
|
||||
Sessions map[[32]byte]*mux.Session
|
||||
Now func() time.Time
|
||||
staticPv crypto.PrivateKey
|
||||
usedRandomM sync.RWMutex
|
||||
usedRandom map[[32]byte]int
|
||||
sessionsM sync.RWMutex
|
||||
sessions map[[32]byte]*mux.Session
|
||||
|
||||
WebServerAddr string
|
||||
}
|
||||
|
||||
func InitState(localHost, localPort, remoteHost, remotePort string, nowFunc func() time.Time) *State {
|
||||
ret := &State{
|
||||
SS_LOCAL_HOST: localHost,
|
||||
SS_LOCAL_PORT: localPort,
|
||||
SS_REMOTE_HOST: remoteHost,
|
||||
SS_REMOTE_PORT: remotePort,
|
||||
Now: nowFunc,
|
||||
}
|
||||
ret.usedRandom = make(map[[32]byte]int)
|
||||
ret.sessions = make(map[[32]byte]*mux.Session)
|
||||
return ret
|
||||
}
|
||||
|
||||
// semi-colon separated value.
|
||||
|
|
@ -65,28 +77,15 @@ func ssvToJson(ssv string) (ret []byte) {
|
|||
return ret
|
||||
}
|
||||
|
||||
// Structue: [D 32 bytes][marshalled public key]
|
||||
func parseKey(b64 string) (*ecies.PrivateKey, error) {
|
||||
// base64 encoded 32 byte private key
|
||||
func parseKey(b64 string) (crypto.PrivateKey, error) {
|
||||
b, err := base64.StdEncoding.DecodeString(b64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := b[0:32]
|
||||
marshalled := b[32:]
|
||||
x, y := elliptic.Unmarshal(ecies.DefaultCurve, marshalled)
|
||||
pub := ecies.PublicKey{
|
||||
X: x,
|
||||
Y: y,
|
||||
Curve: ecies.DefaultCurve,
|
||||
Params: ecies.ParamsFromCurve(ecies.DefaultCurve),
|
||||
}
|
||||
|
||||
pv := &ecies.PrivateKey{
|
||||
PublicKey: pub,
|
||||
D: new(big.Int).SetBytes(d),
|
||||
}
|
||||
return pv, nil
|
||||
var pv [32]byte
|
||||
copy(pv[:], b)
|
||||
return &pv, nil
|
||||
}
|
||||
|
||||
// ParseConfig parses the config (either a path to json or in-line ssv config) into a State variable
|
||||
|
|
@ -109,14 +108,17 @@ func (sta *State) ParseConfig(conf string) (err error) {
|
|||
|
||||
sta.WebServerAddr = preParse.WebServerAddr
|
||||
pv, err := parseKey(preParse.Key)
|
||||
sta.pv = pv
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sta.staticPv = pv
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sta *State) GetSession(SID [32]byte) *mux.Session {
|
||||
sta.SessionsM.Lock()
|
||||
defer sta.SessionsM.Unlock()
|
||||
if sesh, ok := sta.Sessions[SID]; ok {
|
||||
sta.sessionsM.Lock()
|
||||
defer sta.sessionsM.Unlock()
|
||||
if sesh, ok := sta.sessions[SID]; ok {
|
||||
return sesh
|
||||
} else {
|
||||
return nil
|
||||
|
|
@ -124,23 +126,23 @@ func (sta *State) GetSession(SID [32]byte) *mux.Session {
|
|||
}
|
||||
|
||||
func (sta *State) PutSession(SID [32]byte, sesh *mux.Session) {
|
||||
sta.SessionsM.Lock()
|
||||
sta.Sessions[SID] = sesh
|
||||
sta.SessionsM.Unlock()
|
||||
sta.sessionsM.Lock()
|
||||
sta.sessions[SID] = sesh
|
||||
sta.sessionsM.Unlock()
|
||||
}
|
||||
|
||||
func (sta *State) getUsedRandom(random [32]byte) int {
|
||||
sta.UsedRandomM.Lock()
|
||||
defer sta.UsedRandomM.Unlock()
|
||||
return sta.UsedRandom[random]
|
||||
sta.usedRandomM.Lock()
|
||||
defer sta.usedRandomM.Unlock()
|
||||
return sta.usedRandom[random]
|
||||
|
||||
}
|
||||
|
||||
// PutUsedRandom adds a random field into map UsedRandom
|
||||
// PutUsedRandom adds a random field into map usedRandom
|
||||
func (sta *State) putUsedRandom(random [32]byte) {
|
||||
sta.UsedRandomM.Lock()
|
||||
sta.UsedRandom[random] = int(sta.Now().Unix())
|
||||
sta.UsedRandomM.Unlock()
|
||||
sta.usedRandomM.Lock()
|
||||
sta.usedRandom[random] = int(sta.Now().Unix())
|
||||
sta.usedRandomM.Unlock()
|
||||
}
|
||||
|
||||
// UsedRandomCleaner clears the cache of used random fields every 12 hours
|
||||
|
|
@ -148,12 +150,12 @@ func (sta *State) UsedRandomCleaner() {
|
|||
for {
|
||||
time.Sleep(12 * time.Hour)
|
||||
now := int(sta.Now().Unix())
|
||||
sta.UsedRandomM.Lock()
|
||||
for key, t := range sta.UsedRandom {
|
||||
sta.usedRandomM.Lock()
|
||||
for key, t := range sta.usedRandom {
|
||||
if now-t > 12*3600 {
|
||||
delete(sta.UsedRandom, key)
|
||||
delete(sta.usedRandom, key)
|
||||
}
|
||||
}
|
||||
sta.UsedRandomM.Unlock()
|
||||
sta.usedRandomM.Unlock()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ package util
|
|||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
mux "github.com/cbeuw/Cloak/internal/multiplex"
|
||||
)
|
||||
|
||||
func encrypt(iv []byte, key []byte, plaintext []byte) []byte {
|
||||
func AESEncrypt(iv []byte, key []byte, plaintext []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
ciphertext := make([]byte, len(plaintext))
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
|
@ -16,7 +18,7 @@ func encrypt(iv []byte, key []byte, plaintext []byte) []byte {
|
|||
return ciphertext
|
||||
}
|
||||
|
||||
func decrypt(iv []byte, key []byte, ciphertext []byte) []byte {
|
||||
func AESDecrypt(iv []byte, key []byte, ciphertext []byte) []byte {
|
||||
ret := make([]byte, len(ciphertext))
|
||||
copy(ret, ciphertext) // Because XORKeyStream is inplace, but we don't want the input to be changed
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
|
@ -32,8 +34,9 @@ func MakeObfs(key []byte) func(*mux.Frame) []byte {
|
|||
binary.BigEndian.PutUint32(header[4:8], f.Seq)
|
||||
binary.BigEndian.PutUint32(header[8:12], f.ClosingStreamID)
|
||||
// header: [StreamID 4 bytes][Seq 4 bytes][ClosingStreamID 4 bytes]
|
||||
iv := CryptoRandBytes(16)
|
||||
cipherheader := encrypt(iv, key, header)
|
||||
iv := make([]byte, 16)
|
||||
io.ReadFull(rand.Reader, iv)
|
||||
cipherheader := AESEncrypt(iv, key, header)
|
||||
obfsed := make([]byte, len(f.Payload)+12+16)
|
||||
copy(obfsed[0:16], iv)
|
||||
copy(obfsed[16:28], cipherheader)
|
||||
|
|
@ -48,7 +51,7 @@ func MakeObfs(key []byte) func(*mux.Frame) []byte {
|
|||
func MakeDeobfs(key []byte) func([]byte) *mux.Frame {
|
||||
deobfs := func(in []byte) *mux.Frame {
|
||||
peeled := PeelRecordLayer(in)
|
||||
header := decrypt(peeled[0:16], key, peeled[16:28])
|
||||
header := AESDecrypt(peeled[0:16], key, peeled[16:28])
|
||||
streamID := binary.BigEndian.Uint32(header[0:4])
|
||||
seq := binary.BigEndian.Uint32(header[4:8])
|
||||
closingStreamID := binary.BigEndian.Uint32(header[8:12])
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
prand "math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
|
@ -24,18 +22,6 @@ func BtoInt(b []byte) int {
|
|||
return int(sum)
|
||||
}
|
||||
|
||||
// CryptoRandBytes generates a byte slice filled with cryptographically secure random bytes
|
||||
func CryptoRandBytes(length int) []byte {
|
||||
byteMax := big.NewInt(int64(256))
|
||||
ret := make([]byte, length)
|
||||
for i := 0; i < length; i++ {
|
||||
randInt, _ := rand.Int(rand.Reader, byteMax)
|
||||
randByte := byte(randInt.Int64())
|
||||
ret[i] = randByte
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// PsudoRandBytes returns a byte slice filled with psudorandom bytes generated by the seed
|
||||
func PsudoRandBytes(length int, seed int64) []byte {
|
||||
prand.Seed(seed)
|
||||
|
|
@ -65,7 +51,7 @@ func ReadTillDrain(conn net.Conn, buffer []byte) (n int, err error) {
|
|||
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
for left != 0 {
|
||||
if readPtr > len(buffer) || readPtr+left > len(buffer) {
|
||||
err = errors.New("Reading TLS message: actual size greater than header's specification")
|
||||
err = errors.New("Reading TLS message: message size greater than buffer")
|
||||
return
|
||||
}
|
||||
// If left > buffer size (i.e. our message got segmented), the entire MTU is read
|
||||
|
|
@ -82,7 +68,6 @@ func ReadTillDrain(conn net.Conn, buffer []byte) (n int, err error) {
|
|||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
n = 5 + dataLength
|
||||
buffer = buffer[:n]
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue