mirror of https://github.com/cbeuw/Cloak
Merge branch 'master' into notsure2
This commit is contained in:
commit
edb18d5b4c
|
|
@ -73,6 +73,9 @@ func main() {
|
|||
log.Info("Starting standalone mode")
|
||||
}
|
||||
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
})
|
||||
lvl, err := log.ParseLevel(*verbosity)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ func main() {
|
|||
|
||||
var pluginMode bool
|
||||
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
})
|
||||
|
||||
if os.Getenv("SS_LOCAL_HOST") != "" && os.Getenv("SS_LOCAL_PORT") != "" {
|
||||
pluginMode = true
|
||||
config = os.Getenv("SS_PLUGIN_OPTIONS")
|
||||
|
|
|
|||
|
|
@ -65,14 +65,15 @@ func makeSessionPair(numConn int) (*Session, *Session, []*connPair) {
|
|||
|
||||
func runEchoTest(t *testing.T, conns []net.Conn, msgLen int) {
|
||||
var wg sync.WaitGroup
|
||||
testData := make([]byte, msgLen)
|
||||
rand.Read(testData)
|
||||
|
||||
for _, conn := range conns {
|
||||
wg.Add(1)
|
||||
go func(conn net.Conn) {
|
||||
defer wg.Done()
|
||||
|
||||
testData := make([]byte, msgLen)
|
||||
rand.Read(testData)
|
||||
|
||||
// we cannot call t.Fatalf in concurrent contexts
|
||||
n, err := conn.Write(testData)
|
||||
if n != msgLen {
|
||||
|
|
|
|||
|
|
@ -11,11 +11,6 @@ import (
|
|||
"golang.org/x/crypto/salsa20"
|
||||
)
|
||||
|
||||
var u32 = binary.BigEndian.Uint32
|
||||
var u64 = binary.BigEndian.Uint64
|
||||
var putU32 = binary.BigEndian.PutUint32
|
||||
var putU64 = binary.BigEndian.PutUint64
|
||||
|
||||
const frameHeaderLength = 14
|
||||
const salsa20NonceSize = 8
|
||||
|
||||
|
|
@ -98,8 +93,8 @@ func (o *Obfuscator) obfuscate(f *Frame, buf []byte, payloadOffsetInBuf int) (in
|
|||
}
|
||||
|
||||
header := buf[:frameHeaderLength]
|
||||
putU32(header[0:4], f.StreamID)
|
||||
putU64(header[4:12], f.Seq)
|
||||
binary.BigEndian.PutUint32(header[0:4], f.StreamID)
|
||||
binary.BigEndian.PutUint64(header[4:12], f.Seq)
|
||||
header[12] = f.Closing
|
||||
header[13] = byte(extraLen)
|
||||
|
||||
|
|
@ -130,8 +125,8 @@ func (o *Obfuscator) deobfuscate(f *Frame, in []byte) error {
|
|||
nonce := in[len(in)-salsa20NonceSize:]
|
||||
salsa20.XORKeyStream(header, header, nonce, &o.SessionKey)
|
||||
|
||||
streamID := u32(header[0:4])
|
||||
seq := u64(header[4:12])
|
||||
streamID := binary.BigEndian.Uint32(header[0:4])
|
||||
seq := binary.BigEndian.Uint64(header[4:12])
|
||||
closing := header[12]
|
||||
extraLen := header[13]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import (
|
|||
"bytes"
|
||||
"github.com/cbeuw/connutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
|
@ -16,73 +19,188 @@ var seshConfigs = map[string]SessionConfig{
|
|||
"ordered": {},
|
||||
"unordered": {Unordered: true},
|
||||
}
|
||||
var encryptionMethods = map[string]byte{
|
||||
"plain": EncryptionMethodPlain,
|
||||
"aes-256-gcm": EncryptionMethodAES256GCM,
|
||||
"aes-128-gcm": EncryptionMethodAES128GCM,
|
||||
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
|
||||
}
|
||||
|
||||
const testPayloadLen = 1024
|
||||
const obfsBufLen = testPayloadLen * 2
|
||||
|
||||
func TestRecvDataFromRemote(t *testing.T) {
|
||||
testPayload := make([]byte, testPayloadLen)
|
||||
rand.Read(testPayload)
|
||||
f := &Frame{
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
testPayload,
|
||||
}
|
||||
obfsBuf := make([]byte, obfsBufLen)
|
||||
|
||||
var sessionKey [32]byte
|
||||
rand.Read(sessionKey[:])
|
||||
|
||||
MakeObfuscatorUnwrap := func(method byte, sessionKey [32]byte) Obfuscator {
|
||||
ret, err := MakeObfuscator(method, sessionKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make an obfuscator: %v", err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
encryptionMethods := map[string]Obfuscator{
|
||||
"plain": MakeObfuscatorUnwrap(EncryptionMethodPlain, sessionKey),
|
||||
"aes-gcm": MakeObfuscatorUnwrap(EncryptionMethodAES256GCM, sessionKey),
|
||||
"chacha20-poly1305": MakeObfuscatorUnwrap(EncryptionMethodChaha20Poly1305, sessionKey),
|
||||
}
|
||||
|
||||
for seshType, seshConfig := range seshConfigs {
|
||||
seshConfig := seshConfig
|
||||
t.Run(seshType, func(t *testing.T) {
|
||||
for method, obfuscator := range encryptionMethods {
|
||||
obfuscator := obfuscator
|
||||
t.Run(method, func(t *testing.T) {
|
||||
seshConfig.Obfuscator = obfuscator
|
||||
var err error
|
||||
seshConfig.Obfuscator, err = MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make obfuscator: %v", err)
|
||||
}
|
||||
t.Run("initial frame", func(t *testing.T) {
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
n, err := sesh.obfuscate(f, obfsBuf, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
obfsBuf := make([]byte, obfsBufLen)
|
||||
f := Frame{
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
make([]byte, testPayloadLen),
|
||||
}
|
||||
rand.Read(f.Payload)
|
||||
n, err := sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
stream, err := sesh.Accept()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
resultPayload := make([]byte, testPayloadLen)
|
||||
_, err = stream.Read(resultPayload)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, f.Payload, resultPayload)
|
||||
})
|
||||
|
||||
t.Run("two frames in order", func(t *testing.T) {
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
obfsBuf := make([]byte, obfsBufLen)
|
||||
f := Frame{
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
make([]byte, testPayloadLen),
|
||||
}
|
||||
if !bytes.Equal(testPayload, resultPayload) {
|
||||
t.Errorf("Expecting %x, got %x", testPayload, resultPayload)
|
||||
rand.Read(f.Payload)
|
||||
n, err := sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
assert.NoError(t, err)
|
||||
stream, err := sesh.Accept()
|
||||
assert.NoError(t, err)
|
||||
|
||||
resultPayload := make([]byte, testPayloadLen)
|
||||
_, err = io.ReadFull(stream, resultPayload)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, f.Payload, resultPayload)
|
||||
|
||||
f.Seq += 1
|
||||
rand.Read(f.Payload)
|
||||
n, err = sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = io.ReadFull(stream, resultPayload)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, f.Payload, resultPayload)
|
||||
})
|
||||
|
||||
t.Run("two frames in order", func(t *testing.T) {
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
obfsBuf := make([]byte, obfsBufLen)
|
||||
f := Frame{
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
make([]byte, testPayloadLen),
|
||||
}
|
||||
rand.Read(f.Payload)
|
||||
n, err := sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
assert.NoError(t, err)
|
||||
stream, err := sesh.Accept()
|
||||
assert.NoError(t, err)
|
||||
|
||||
resultPayload := make([]byte, testPayloadLen)
|
||||
_, err = io.ReadFull(stream, resultPayload)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, f.Payload, resultPayload)
|
||||
|
||||
f.Seq += 1
|
||||
rand.Read(f.Payload)
|
||||
n, err = sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = io.ReadFull(stream, resultPayload)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, f.Payload, resultPayload)
|
||||
})
|
||||
|
||||
if seshType == "ordered" {
|
||||
t.Run("frames out of order", func(t *testing.T) {
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
obfsBuf := make([]byte, obfsBufLen)
|
||||
f := Frame{
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
}
|
||||
|
||||
// First frame
|
||||
seq0 := make([]byte, testPayloadLen)
|
||||
rand.Read(seq0)
|
||||
f.Seq = 0
|
||||
f.Payload = seq0
|
||||
n, err := sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Third frame
|
||||
seq2 := make([]byte, testPayloadLen)
|
||||
rand.Read(seq2)
|
||||
f.Seq = 2
|
||||
f.Payload = seq2
|
||||
n, err = sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Second frame
|
||||
seq1 := make([]byte, testPayloadLen)
|
||||
rand.Read(seq1)
|
||||
f.Seq = 1
|
||||
f.Payload = seq1
|
||||
n, err = sesh.obfuscate(&f, obfsBuf, 0)
|
||||
assert.NoError(t, err)
|
||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Expect things to receive in order
|
||||
stream, err := sesh.Accept()
|
||||
assert.NoError(t, err)
|
||||
|
||||
resultPayload := make([]byte, testPayloadLen)
|
||||
|
||||
// First
|
||||
_, err = io.ReadFull(stream, resultPayload)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, seq0, resultPayload)
|
||||
|
||||
// Second
|
||||
_, err = io.ReadFull(stream, resultPayload)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, seq1, resultPayload)
|
||||
|
||||
// Third
|
||||
_, err = io.ReadFull(stream, resultPayload)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, seq2, resultPayload)
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -94,10 +212,9 @@ func TestRecvDataFromRemote_Closing_InOrder(t *testing.T) {
|
|||
|
||||
var sessionKey [32]byte
|
||||
rand.Read(sessionKey[:])
|
||||
obfuscator, _ := MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||
|
||||
seshConfig := seshConfigs["ordered"]
|
||||
seshConfig.Obfuscator = obfuscator
|
||||
seshConfig.Obfuscator, _ = MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
|
||||
f1 := &Frame{
|
||||
|
|
@ -233,10 +350,9 @@ func TestRecvDataFromRemote_Closing_OutOfOrder(t *testing.T) {
|
|||
|
||||
var sessionKey [32]byte
|
||||
rand.Read(sessionKey[:])
|
||||
obfuscator, _ := MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||
|
||||
seshConfig := seshConfigs["ordered"]
|
||||
seshConfig.Obfuscator = obfuscator
|
||||
seshConfig.Obfuscator, _ = MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
|
||||
// receive stream 1 closing first
|
||||
|
|
@ -415,7 +531,7 @@ func TestSession_timeoutAfter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
|
||||
func BenchmarkRecvDataFromRemote(b *testing.B) {
|
||||
testPayload := make([]byte, testPayloadLen)
|
||||
rand.Read(testPayload)
|
||||
f := &Frame{
|
||||
|
|
@ -428,22 +544,20 @@ func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
|
|||
var sessionKey [32]byte
|
||||
rand.Read(sessionKey[:])
|
||||
|
||||
table := map[string]byte{
|
||||
"plain": EncryptionMethodPlain,
|
||||
"aes-256-gcm": EncryptionMethodAES256GCM,
|
||||
"aes-128-gcm": EncryptionMethodAES128GCM,
|
||||
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
|
||||
}
|
||||
|
||||
const maxIter = 100_000 // run with -benchtime 100000x to avoid index out of bounds panic
|
||||
for name, ep := range table {
|
||||
for name, ep := range encryptionMethods {
|
||||
ep := ep
|
||||
b.Run(name, func(b *testing.B) {
|
||||
seshConfig := seshConfigs["ordered"]
|
||||
obfuscator, _ := MakeObfuscator(ep, sessionKey)
|
||||
seshConfig.Obfuscator = obfuscator
|
||||
for seshType, seshConfig := range seshConfigs {
|
||||
b.Run(seshType, func(b *testing.B) {
|
||||
seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
|
||||
go func() {
|
||||
stream, _ := sesh.Accept()
|
||||
stream.(*Stream).WriteTo(ioutil.Discard)
|
||||
}()
|
||||
|
||||
binaryFrames := [maxIter][]byte{}
|
||||
for i := 0; i < maxIter; i++ {
|
||||
obfsBuf := make([]byte, obfsBufLen)
|
||||
|
|
@ -459,28 +573,21 @@ func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
|
|||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMultiStreamWrite(b *testing.B) {
|
||||
var sessionKey [32]byte
|
||||
rand.Read(sessionKey[:])
|
||||
|
||||
table := map[string]byte{
|
||||
"plain": EncryptionMethodPlain,
|
||||
"aes-256-gcm": EncryptionMethodAES256GCM,
|
||||
"aes-128-gcm": EncryptionMethodAES128GCM,
|
||||
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
|
||||
}
|
||||
|
||||
testPayload := make([]byte, testPayloadLen)
|
||||
|
||||
for name, ep := range table {
|
||||
for name, ep := range encryptionMethods {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for seshType, seshConfig := range seshConfigs {
|
||||
seshConfig := seshConfig
|
||||
b.Run(seshType, func(b *testing.B) {
|
||||
obfuscator, _ := MakeObfuscator(ep, sessionKey)
|
||||
seshConfig.Obfuscator = obfuscator
|
||||
seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
|
||||
sesh := MakeSession(0, seshConfig)
|
||||
sesh.AddConnection(connutil.Discard())
|
||||
b.ResetTimer()
|
||||
|
|
@ -496,3 +603,36 @@ func BenchmarkMultiStreamWrite(b *testing.B) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLatency(b *testing.B) {
|
||||
var sessionKey [32]byte
|
||||
rand.Read(sessionKey[:])
|
||||
|
||||
for name, ep := range encryptionMethods {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for seshType, seshConfig := range seshConfigs {
|
||||
b.Run(seshType, func(b *testing.B) {
|
||||
seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
|
||||
clientSesh := MakeSession(0, seshConfig)
|
||||
serverSesh := MakeSession(0, seshConfig)
|
||||
|
||||
c, s := net.Pipe()
|
||||
clientSesh.AddConnection(c)
|
||||
serverSesh.AddConnection(s)
|
||||
|
||||
buf := make([]byte, 64)
|
||||
clientStream, _ := clientSesh.OpenStream()
|
||||
clientStream.Write(buf)
|
||||
serverStream, _ := serverSesh.Accept()
|
||||
io.ReadFull(serverStream, buf)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
clientStream.Write(buf)
|
||||
io.ReadFull(serverStream, buf)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ func (sb *streamBuffer) Write(f *Frame) (toBeClosed bool, err error) {
|
|||
}
|
||||
|
||||
saved := *f
|
||||
saved.Payload = make([]byte, len(f.Payload))
|
||||
copy(saved.Payload, f.Payload)
|
||||
heap.Push(&sb.sh, &saved)
|
||||
// Keep popping from the heap until empty or to the point that the wanted seq was not received
|
||||
for len(sb.sh) > 0 && sb.sh[0].Seq == sb.nextRecvSeq {
|
||||
|
|
|
|||
|
|
@ -222,14 +222,15 @@ func establishSession(lcc client.LocalConnConfig, rcc client.RemoteConnConfig, a
|
|||
|
||||
func runEchoTest(t *testing.T, conns []net.Conn, msgLen int) {
|
||||
var wg sync.WaitGroup
|
||||
testData := make([]byte, msgLen)
|
||||
rand.Read(testData)
|
||||
|
||||
for _, conn := range conns {
|
||||
wg.Add(1)
|
||||
go func(conn net.Conn) {
|
||||
defer wg.Done()
|
||||
|
||||
testData := make([]byte, msgLen)
|
||||
rand.Read(testData)
|
||||
|
||||
// we cannot call t.Fatalf in concurrent contexts
|
||||
n, err := conn.Write(testData)
|
||||
if n != msgLen {
|
||||
|
|
|
|||
Loading…
Reference in New Issue