mirror of https://github.com/cbeuw/Cloak
Merge branch 'master' into notsure2
This commit is contained in:
commit
008fcbd17d
|
|
@ -11,9 +11,6 @@ import (
|
||||||
"golang.org/x/crypto/salsa20"
|
"golang.org/x/crypto/salsa20"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Obfser func(*Frame, []byte, int) (int, error)
|
|
||||||
type Deobfser func(*Frame, []byte) error
|
|
||||||
|
|
||||||
const frameHeaderLength = 14
|
const frameHeaderLength = 14
|
||||||
const salsa20NonceSize = 8
|
const salsa20NonceSize = 8
|
||||||
|
|
||||||
|
|
@ -26,25 +23,19 @@ const (
|
||||||
|
|
||||||
// Obfuscator is responsible for serialisation, obfuscation, and optional encryption of data frames.
|
// Obfuscator is responsible for serialisation, obfuscation, and optional encryption of data frames.
|
||||||
type Obfuscator struct {
|
type Obfuscator struct {
|
||||||
// Used in Stream.Write. Add multiplexing headers, encrypt and add TLS header
|
payloadCipher cipher.AEAD
|
||||||
Obfs Obfser
|
|
||||||
// Remove TLS header, decrypt and unmarshall frames
|
sessionKey [32]byte
|
||||||
Deobfs Deobfser
|
|
||||||
SessionKey [32]byte
|
|
||||||
|
|
||||||
maxOverhead int
|
maxOverhead int
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeObfs returns a function of type Obfser. An Obfser takes three arguments:
|
// obfuscate adds multiplexing headers, encrypt and add TLS header
|
||||||
// a *Frame with all the field set correctly, a []byte as buffer to put encrypted
|
func (o *Obfuscator) obfuscate(f *Frame, buf []byte, payloadOffsetInBuf int) (int, error) {
|
||||||
// message in, and an int called payloadOffsetInBuf to be used when *Frame.payload
|
|
||||||
// is in the byte slice used as buffer (2nd argument). payloadOffsetInBuf specifies
|
|
||||||
// the index at which data belonging to *Frame.Payload starts in the buffer.
|
|
||||||
func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Obfser {
|
|
||||||
// The method here is to use the first payloadCipher.NonceSize() bytes of the serialised frame header
|
// The method here is to use the first payloadCipher.NonceSize() bytes of the serialised frame header
|
||||||
// as iv/nonce for the AEAD cipher to encrypt the frame payload. Then we use
|
// as iv/nonce for the AEAD cipher to encrypt the frame payload. Then we use
|
||||||
// the authentication tag produced appended to the end of the ciphertext (of size payloadCipher.Overhead())
|
// the authentication tag produced appended to the end of the ciphertext (of size payloadCipher.Overhead())
|
||||||
// as nonce for Salsa20 to encrypt the frame header. Both with SessionKey as keys.
|
// as nonce for Salsa20 to encrypt the frame header. Both with sessionKey as keys.
|
||||||
//
|
//
|
||||||
// Several cryptographic guarantees we have made here: that payloadCipher, as an AEAD, is given a unique
|
// Several cryptographic guarantees we have made here: that payloadCipher, as an AEAD, is given a unique
|
||||||
// iv/nonce each time, relative to its key; that the frame header encryptor Salsa20 is given a unique
|
// iv/nonce each time, relative to its key; that the frame header encryptor Salsa20 is given a unique
|
||||||
|
|
@ -72,20 +63,19 @@ func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Obfser {
|
||||||
// We can't ensure its uniqueness ourselves, which is why plaintext mode must only be used when the user input
|
// We can't ensure its uniqueness ourselves, which is why plaintext mode must only be used when the user input
|
||||||
// is already random-like. For Cloak it would normally mean that the user is using a proxy protocol that sends
|
// is already random-like. For Cloak it would normally mean that the user is using a proxy protocol that sends
|
||||||
// encrypted data.
|
// encrypted data.
|
||||||
obfs := func(f *Frame, buf []byte, payloadOffsetInBuf int) (int, error) {
|
|
||||||
payloadLen := len(f.Payload)
|
payloadLen := len(f.Payload)
|
||||||
if payloadLen == 0 {
|
if payloadLen == 0 {
|
||||||
return 0, errors.New("payload cannot be empty")
|
return 0, errors.New("payload cannot be empty")
|
||||||
}
|
}
|
||||||
var extraLen int
|
var extraLen int
|
||||||
if payloadCipher == nil {
|
if o.payloadCipher == nil {
|
||||||
extraLen = salsa20NonceSize - payloadLen
|
extraLen = salsa20NonceSize - payloadLen
|
||||||
if extraLen < 0 {
|
if extraLen < 0 {
|
||||||
// if our payload is already greater than 8 bytes
|
// if our payload is already greater than 8 bytes
|
||||||
extraLen = 0
|
extraLen = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
extraLen = payloadCipher.Overhead()
|
extraLen = o.payloadCipher.Overhead()
|
||||||
if extraLen < salsa20NonceSize {
|
if extraLen < salsa20NonceSize {
|
||||||
return 0, errors.New("AEAD's Overhead cannot be fewer than 8 bytes")
|
return 0, errors.New("AEAD's Overhead cannot be fewer than 8 bytes")
|
||||||
}
|
}
|
||||||
|
|
@ -108,39 +98,32 @@ func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Obfser {
|
||||||
header[12] = f.Closing
|
header[12] = f.Closing
|
||||||
header[13] = byte(extraLen)
|
header[13] = byte(extraLen)
|
||||||
|
|
||||||
if payloadCipher == nil {
|
if o.payloadCipher == nil {
|
||||||
if extraLen != 0 { // read nonce
|
if extraLen != 0 { // read nonce
|
||||||
extra := buf[usefulLen-extraLen : usefulLen]
|
extra := buf[usefulLen-extraLen : usefulLen]
|
||||||
common.CryptoRandRead(extra)
|
common.CryptoRandRead(extra)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
payloadCipher.Seal(payload[:0], header[:payloadCipher.NonceSize()], payload, nil)
|
o.payloadCipher.Seal(payload[:0], header[:o.payloadCipher.NonceSize()], payload, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce := buf[usefulLen-salsa20NonceSize : usefulLen]
|
nonce := buf[usefulLen-salsa20NonceSize : usefulLen]
|
||||||
salsa20.XORKeyStream(header, header, nonce, &salsaKey)
|
salsa20.XORKeyStream(header, header, nonce, &o.sessionKey)
|
||||||
|
|
||||||
return usefulLen, nil
|
return usefulLen, nil
|
||||||
}
|
|
||||||
return obfs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeDeobfs returns a function Deobfser. A Deobfser takes in a single byte slice,
|
// deobfuscate removes TLS header, decrypt and unmarshall frames
|
||||||
// containing the message to be decrypted, and returns a *Frame containing the frame
|
func (o *Obfuscator) deobfuscate(f *Frame, in []byte) error {
|
||||||
// information and plaintext
|
if len(in) < frameHeaderLength+salsa20NonceSize {
|
||||||
func MakeDeobfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Deobfser {
|
return fmt.Errorf("input size %v, but it cannot be shorter than %v bytes", len(in), frameHeaderLength+salsa20NonceSize)
|
||||||
// frame header length + minimum data size (i.e. nonce size of salsa20)
|
|
||||||
const minInputLen = frameHeaderLength + salsa20NonceSize
|
|
||||||
deobfs := func(f *Frame, in []byte) error {
|
|
||||||
if len(in) < minInputLen {
|
|
||||||
return fmt.Errorf("input size %v, but it cannot be shorter than %v bytes", len(in), minInputLen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header := in[:frameHeaderLength]
|
header := in[:frameHeaderLength]
|
||||||
pldWithOverHead := in[frameHeaderLength:] // payload + potential overhead
|
pldWithOverHead := in[frameHeaderLength:] // payload + potential overhead
|
||||||
|
|
||||||
nonce := in[len(in)-salsa20NonceSize:]
|
nonce := in[len(in)-salsa20NonceSize:]
|
||||||
salsa20.XORKeyStream(header, header, nonce, &salsaKey)
|
salsa20.XORKeyStream(header, header, nonce, &o.sessionKey)
|
||||||
|
|
||||||
streamID := binary.BigEndian.Uint32(header[0:4])
|
streamID := binary.BigEndian.Uint32(header[0:4])
|
||||||
seq := binary.BigEndian.Uint64(header[4:12])
|
seq := binary.BigEndian.Uint64(header[4:12])
|
||||||
|
|
@ -154,14 +137,14 @@ func MakeDeobfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Deobfser {
|
||||||
|
|
||||||
var outputPayload []byte
|
var outputPayload []byte
|
||||||
|
|
||||||
if payloadCipher == nil {
|
if o.payloadCipher == nil {
|
||||||
if extraLen == 0 {
|
if extraLen == 0 {
|
||||||
outputPayload = pldWithOverHead
|
outputPayload = pldWithOverHead
|
||||||
} else {
|
} else {
|
||||||
outputPayload = pldWithOverHead[:usefulPayloadLen]
|
outputPayload = pldWithOverHead[:usefulPayloadLen]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err := payloadCipher.Open(pldWithOverHead[:0], header[:payloadCipher.NonceSize()], pldWithOverHead, nil)
|
_, err := o.payloadCipher.Open(pldWithOverHead[:0], header[:o.payloadCipher.NonceSize()], pldWithOverHead, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -173,58 +156,53 @@ func MakeDeobfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Deobfser {
|
||||||
f.Closing = closing
|
f.Closing = closing
|
||||||
f.Payload = outputPayload
|
f.Payload = outputPayload
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
return deobfs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeObfuscator(encryptionMethod byte, sessionKey [32]byte) (obfuscator Obfuscator, err error) {
|
func MakeObfuscator(encryptionMethod byte, sessionKey [32]byte) (o Obfuscator, err error) {
|
||||||
obfuscator = Obfuscator{
|
o = Obfuscator{
|
||||||
SessionKey: sessionKey,
|
sessionKey: sessionKey,
|
||||||
}
|
}
|
||||||
var payloadCipher cipher.AEAD
|
|
||||||
switch encryptionMethod {
|
switch encryptionMethod {
|
||||||
case EncryptionMethodPlain:
|
case EncryptionMethodPlain:
|
||||||
payloadCipher = nil
|
o.payloadCipher = nil
|
||||||
obfuscator.maxOverhead = salsa20NonceSize
|
o.maxOverhead = salsa20NonceSize
|
||||||
case EncryptionMethodAES256GCM:
|
case EncryptionMethodAES256GCM:
|
||||||
var c cipher.Block
|
var c cipher.Block
|
||||||
c, err = aes.NewCipher(sessionKey[:])
|
c, err = aes.NewCipher(sessionKey[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
payloadCipher, err = cipher.NewGCM(c)
|
o.payloadCipher, err = cipher.NewGCM(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
obfuscator.maxOverhead = payloadCipher.Overhead()
|
o.maxOverhead = o.payloadCipher.Overhead()
|
||||||
case EncryptionMethodAES128GCM:
|
case EncryptionMethodAES128GCM:
|
||||||
var c cipher.Block
|
var c cipher.Block
|
||||||
c, err = aes.NewCipher(sessionKey[:16])
|
c, err = aes.NewCipher(sessionKey[:16])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
payloadCipher, err = cipher.NewGCM(c)
|
o.payloadCipher, err = cipher.NewGCM(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
obfuscator.maxOverhead = payloadCipher.Overhead()
|
o.maxOverhead = o.payloadCipher.Overhead()
|
||||||
case EncryptionMethodChaha20Poly1305:
|
case EncryptionMethodChaha20Poly1305:
|
||||||
payloadCipher, err = chacha20poly1305.New(sessionKey[:])
|
o.payloadCipher, err = chacha20poly1305.New(sessionKey[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
obfuscator.maxOverhead = payloadCipher.Overhead()
|
o.maxOverhead = o.payloadCipher.Overhead()
|
||||||
default:
|
default:
|
||||||
return obfuscator, errors.New("Unknown encryption method")
|
return o, fmt.Errorf("unknown encryption method valued %v", encryptionMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
if payloadCipher != nil {
|
if o.payloadCipher != nil {
|
||||||
if payloadCipher.NonceSize() > frameHeaderLength {
|
if o.payloadCipher.NonceSize() > frameHeaderLength {
|
||||||
return obfuscator, errors.New("payload AEAD's nonce size cannot be greater than size of frame header")
|
return o, errors.New("payload AEAD's nonce size cannot be greater than size of frame header")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obfuscator.Obfs = MakeObfs(sessionKey, payloadCipher)
|
|
||||||
obfuscator.Deobfs = MakeDeobfs(sessionKey, payloadCipher)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package multiplex
|
package multiplex
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -15,69 +15,119 @@ func TestGenerateObfs(t *testing.T) {
|
||||||
var sessionKey [32]byte
|
var sessionKey [32]byte
|
||||||
rand.Read(sessionKey[:])
|
rand.Read(sessionKey[:])
|
||||||
|
|
||||||
run := func(obfuscator Obfuscator, ct *testing.T) {
|
run := func(o Obfuscator, t *testing.T) {
|
||||||
obfsBuf := make([]byte, 512)
|
obfsBuf := make([]byte, 512)
|
||||||
_testFrame, _ := quick.Value(reflect.TypeOf(&Frame{}), rand.New(rand.NewSource(42)))
|
_testFrame, _ := quick.Value(reflect.TypeOf(Frame{}), rand.New(rand.NewSource(42)))
|
||||||
testFrame := _testFrame.Interface().(*Frame)
|
testFrame := _testFrame.Interface().(Frame)
|
||||||
i, err := obfuscator.Obfs(testFrame, obfsBuf, 0)
|
i, err := o.obfuscate(&testFrame, obfsBuf, 0)
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
ct.Error("failed to obfs ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultFrame Frame
|
var resultFrame Frame
|
||||||
err = obfuscator.Deobfs(&resultFrame, obfsBuf[:i])
|
|
||||||
if err != nil {
|
err = o.deobfuscate(&resultFrame, obfsBuf[:i])
|
||||||
ct.Error("failed to deobfs ", err)
|
assert.NoError(t, err)
|
||||||
return
|
assert.EqualValues(t, testFrame, resultFrame)
|
||||||
}
|
|
||||||
if !bytes.Equal(testFrame.Payload, resultFrame.Payload) || testFrame.StreamID != resultFrame.StreamID {
|
|
||||||
ct.Error("expecting", testFrame,
|
|
||||||
"got", resultFrame)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("plain", func(t *testing.T) {
|
t.Run("plain", func(t *testing.T) {
|
||||||
obfuscator, err := MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
o, err := MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Errorf("failed to generate obfuscator %v", err)
|
run(o, t)
|
||||||
} else {
|
|
||||||
run(obfuscator, t)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
t.Run("aes-256-gcm", func(t *testing.T) {
|
t.Run("aes-256-gcm", func(t *testing.T) {
|
||||||
obfuscator, err := MakeObfuscator(EncryptionMethodAES256GCM, sessionKey)
|
o, err := MakeObfuscator(EncryptionMethodAES256GCM, sessionKey)
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Errorf("failed to generate obfuscator %v", err)
|
run(o, t)
|
||||||
} else {
|
|
||||||
run(obfuscator, t)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
t.Run("aes-128-gcm", func(t *testing.T) {
|
t.Run("aes-128-gcm", func(t *testing.T) {
|
||||||
obfuscator, err := MakeObfuscator(EncryptionMethodAES128GCM, sessionKey)
|
o, err := MakeObfuscator(EncryptionMethodAES128GCM, sessionKey)
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Errorf("failed to generate obfuscator %v", err)
|
run(o, t)
|
||||||
} else {
|
|
||||||
run(obfuscator, t)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
t.Run("chacha20-poly1305", func(t *testing.T) {
|
t.Run("chacha20-poly1305", func(t *testing.T) {
|
||||||
obfuscator, err := MakeObfuscator(EncryptionMethodChaha20Poly1305, sessionKey)
|
o, err := MakeObfuscator(EncryptionMethodChaha20Poly1305, sessionKey)
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Errorf("failed to generate obfuscator %v", err)
|
run(o, t)
|
||||||
} else {
|
|
||||||
run(obfuscator, t)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
t.Run("unknown encryption method", func(t *testing.T) {
|
t.Run("unknown encryption method", func(t *testing.T) {
|
||||||
_, err := MakeObfuscator(0xff, sessionKey)
|
_, err := MakeObfuscator(0xff, sessionKey)
|
||||||
if err == nil {
|
assert.Error(t, err)
|
||||||
t.Errorf("unknown encryption mehtod error expected")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestObfuscate(t *testing.T) {
|
||||||
|
var sessionKey [32]byte
|
||||||
|
rand.Read(sessionKey[:])
|
||||||
|
|
||||||
|
const testPayloadLen = 1024
|
||||||
|
testPayload := make([]byte, testPayloadLen)
|
||||||
|
rand.Read(testPayload)
|
||||||
|
f := Frame{
|
||||||
|
StreamID: 0,
|
||||||
|
Seq: 0,
|
||||||
|
Closing: 0,
|
||||||
|
Payload: testPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
runTest := func(t *testing.T, o Obfuscator) {
|
||||||
|
obfsBuf := make([]byte, testPayloadLen*2)
|
||||||
|
n, err := o.obfuscate(&f, obfsBuf, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
resultFrame := Frame{}
|
||||||
|
err = o.deobfuscate(&resultFrame, obfsBuf[:n])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.EqualValues(t, f, resultFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("plain", func(t *testing.T) {
|
||||||
|
o := Obfuscator{
|
||||||
|
payloadCipher: nil,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
maxOverhead: salsa20NonceSize,
|
||||||
|
}
|
||||||
|
runTest(t, o)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("aes-128-gcm", func(t *testing.T) {
|
||||||
|
c, err := aes.NewCipher(sessionKey[:16])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
payloadCipher, err := cipher.NewGCM(c)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
o := Obfuscator{
|
||||||
|
payloadCipher: payloadCipher,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
|
runTest(t, o)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("aes-256-gcm", func(t *testing.T) {
|
||||||
|
c, err := aes.NewCipher(sessionKey[:])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
payloadCipher, err := cipher.NewGCM(c)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
o := Obfuscator{
|
||||||
|
payloadCipher: payloadCipher,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
|
runTest(t, o)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("chacha20-poly1305", func(t *testing.T) {
|
||||||
|
payloadCipher, err := chacha20poly1305.New(sessionKey[:])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
o := Obfuscator{
|
||||||
|
payloadCipher: payloadCipher,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
|
runTest(t, o)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkObfs(b *testing.B) {
|
func BenchmarkObfs(b *testing.B) {
|
||||||
testPayload := make([]byte, 1024)
|
testPayload := make([]byte, 1024)
|
||||||
rand.Read(testPayload)
|
rand.Read(testPayload)
|
||||||
|
|
@ -96,40 +146,57 @@ func BenchmarkObfs(b *testing.B) {
|
||||||
c, _ := aes.NewCipher(key[:])
|
c, _ := aes.NewCipher(key[:])
|
||||||
payloadCipher, _ := cipher.NewGCM(c)
|
payloadCipher, _ := cipher.NewGCM(c)
|
||||||
|
|
||||||
obfs := MakeObfs(key, payloadCipher)
|
obfuscator := Obfuscator{
|
||||||
|
payloadCipher: payloadCipher,
|
||||||
|
sessionKey: key,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
|
|
||||||
b.SetBytes(int64(len(testFrame.Payload)))
|
b.SetBytes(int64(len(testFrame.Payload)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
obfs(testFrame, obfsBuf, 0)
|
obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
b.Run("AES128GCM", func(b *testing.B) {
|
b.Run("AES128GCM", func(b *testing.B) {
|
||||||
c, _ := aes.NewCipher(key[:16])
|
c, _ := aes.NewCipher(key[:16])
|
||||||
payloadCipher, _ := cipher.NewGCM(c)
|
payloadCipher, _ := cipher.NewGCM(c)
|
||||||
|
|
||||||
obfs := MakeObfs(key, payloadCipher)
|
obfuscator := Obfuscator{
|
||||||
|
payloadCipher: payloadCipher,
|
||||||
|
sessionKey: key,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
b.SetBytes(int64(len(testFrame.Payload)))
|
b.SetBytes(int64(len(testFrame.Payload)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
obfs(testFrame, obfsBuf, 0)
|
obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
b.Run("plain", func(b *testing.B) {
|
b.Run("plain", func(b *testing.B) {
|
||||||
obfs := MakeObfs(key, nil)
|
obfuscator := Obfuscator{
|
||||||
|
payloadCipher: nil,
|
||||||
|
sessionKey: key,
|
||||||
|
maxOverhead: salsa20NonceSize,
|
||||||
|
}
|
||||||
b.SetBytes(int64(len(testFrame.Payload)))
|
b.SetBytes(int64(len(testFrame.Payload)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
obfs(testFrame, obfsBuf, 0)
|
obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
b.Run("chacha20Poly1305", func(b *testing.B) {
|
b.Run("chacha20Poly1305", func(b *testing.B) {
|
||||||
payloadCipher, _ := chacha20poly1305.New(key[:16])
|
payloadCipher, _ := chacha20poly1305.New(key[:])
|
||||||
|
|
||||||
obfs := MakeObfs(key, payloadCipher)
|
obfuscator := Obfuscator{
|
||||||
|
payloadCipher: payloadCipher,
|
||||||
|
sessionKey: key,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
b.SetBytes(int64(len(testFrame.Payload)))
|
b.SetBytes(int64(len(testFrame.Payload)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
obfs(testFrame, obfsBuf, 0)
|
obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -151,57 +218,70 @@ func BenchmarkDeobfs(b *testing.B) {
|
||||||
b.Run("AES256GCM", func(b *testing.B) {
|
b.Run("AES256GCM", func(b *testing.B) {
|
||||||
c, _ := aes.NewCipher(key[:])
|
c, _ := aes.NewCipher(key[:])
|
||||||
payloadCipher, _ := cipher.NewGCM(c)
|
payloadCipher, _ := cipher.NewGCM(c)
|
||||||
|
obfuscator := Obfuscator{
|
||||||
|
payloadCipher: payloadCipher,
|
||||||
|
sessionKey: key,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
|
|
||||||
obfs := MakeObfs(key, payloadCipher)
|
n, _ := obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
n, _ := obfs(testFrame, obfsBuf, 0)
|
|
||||||
deobfs := MakeDeobfs(key, payloadCipher)
|
|
||||||
|
|
||||||
frame := new(Frame)
|
frame := new(Frame)
|
||||||
b.SetBytes(int64(n))
|
b.SetBytes(int64(n))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
deobfs(frame, obfsBuf[:n])
|
obfuscator.deobfuscate(frame, obfsBuf[:n])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
b.Run("AES128GCM", func(b *testing.B) {
|
b.Run("AES128GCM", func(b *testing.B) {
|
||||||
c, _ := aes.NewCipher(key[:16])
|
c, _ := aes.NewCipher(key[:16])
|
||||||
payloadCipher, _ := cipher.NewGCM(c)
|
payloadCipher, _ := cipher.NewGCM(c)
|
||||||
|
|
||||||
obfs := MakeObfs(key, payloadCipher)
|
obfuscator := Obfuscator{
|
||||||
n, _ := obfs(testFrame, obfsBuf, 0)
|
payloadCipher: payloadCipher,
|
||||||
deobfs := MakeDeobfs(key, payloadCipher)
|
sessionKey: key,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
|
n, _ := obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
|
|
||||||
frame := new(Frame)
|
frame := new(Frame)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(n))
|
b.SetBytes(int64(n))
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
deobfs(frame, obfsBuf[:n])
|
obfuscator.deobfuscate(frame, obfsBuf[:n])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
b.Run("plain", func(b *testing.B) {
|
b.Run("plain", func(b *testing.B) {
|
||||||
obfs := MakeObfs(key, nil)
|
obfuscator := Obfuscator{
|
||||||
n, _ := obfs(testFrame, obfsBuf, 0)
|
payloadCipher: nil,
|
||||||
deobfs := MakeDeobfs(key, nil)
|
sessionKey: key,
|
||||||
|
maxOverhead: salsa20NonceSize,
|
||||||
|
}
|
||||||
|
n, _ := obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
|
|
||||||
frame := new(Frame)
|
frame := new(Frame)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(n))
|
b.SetBytes(int64(n))
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
deobfs(frame, obfsBuf[:n])
|
obfuscator.deobfuscate(frame, obfsBuf[:n])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
b.Run("chacha20Poly1305", func(b *testing.B) {
|
b.Run("chacha20Poly1305", func(b *testing.B) {
|
||||||
payloadCipher, _ := chacha20poly1305.New(key[:16])
|
payloadCipher, _ := chacha20poly1305.New(key[:])
|
||||||
|
|
||||||
obfs := MakeObfs(key, payloadCipher)
|
obfuscator := Obfuscator{
|
||||||
n, _ := obfs(testFrame, obfsBuf, 0)
|
payloadCipher: nil,
|
||||||
deobfs := MakeDeobfs(key, payloadCipher)
|
sessionKey: key,
|
||||||
|
maxOverhead: payloadCipher.Overhead(),
|
||||||
|
}
|
||||||
|
|
||||||
|
n, _ := obfuscator.obfuscate(testFrame, obfsBuf, 0)
|
||||||
|
|
||||||
frame := new(Frame)
|
frame := new(Frame)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(n))
|
b.SetBytes(int64(n))
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
deobfs(frame, obfsBuf[:n])
|
obfuscator.deobfuscate(frame, obfsBuf[:n])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,10 @@ func MakeSession(id uint32, config SessionConfig) *Session {
|
||||||
return sesh
|
return sesh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sesh *Session) GetSessionKey() [32]byte {
|
||||||
|
return sesh.sessionKey
|
||||||
|
}
|
||||||
|
|
||||||
func (sesh *Session) streamCountIncr() uint32 {
|
func (sesh *Session) streamCountIncr() uint32 {
|
||||||
return atomic.AddUint32(&sesh.activeStreamCount, 1)
|
return atomic.AddUint32(&sesh.activeStreamCount, 1)
|
||||||
}
|
}
|
||||||
|
|
@ -232,7 +236,7 @@ func (sesh *Session) recvDataFromRemote(data []byte) error {
|
||||||
frame := sesh.recvFramePool.Get().(*Frame)
|
frame := sesh.recvFramePool.Get().(*Frame)
|
||||||
defer sesh.recvFramePool.Put(frame)
|
defer sesh.recvFramePool.Put(frame)
|
||||||
|
|
||||||
err := sesh.Deobfs(frame, data)
|
err := sesh.deobfuscate(frame, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to decrypt a frame for session %v: %v", sesh.id, err)
|
return fmt.Errorf("Failed to decrypt a frame for session %v: %v", sesh.id, err)
|
||||||
}
|
}
|
||||||
|
|
@ -331,7 +335,7 @@ func (sesh *Session) Close() error {
|
||||||
Closing: closingSession,
|
Closing: closingSession,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
i, err := sesh.Obfs(f, *buf, frameHeaderLength)
|
i, err := sesh.obfuscate(f, *buf, frameHeaderLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,10 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/cbeuw/connutil"
|
"github.com/cbeuw/connutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -16,73 +19,188 @@ var seshConfigs = map[string]SessionConfig{
|
||||||
"ordered": {},
|
"ordered": {},
|
||||||
"unordered": {Unordered: true},
|
"unordered": {Unordered: true},
|
||||||
}
|
}
|
||||||
|
var encryptionMethods = map[string]byte{
|
||||||
|
"plain": EncryptionMethodPlain,
|
||||||
|
"aes-256-gcm": EncryptionMethodAES256GCM,
|
||||||
|
"aes-128-gcm": EncryptionMethodAES128GCM,
|
||||||
|
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
|
||||||
|
}
|
||||||
|
|
||||||
const testPayloadLen = 1024
|
const testPayloadLen = 1024
|
||||||
const obfsBufLen = testPayloadLen * 2
|
const obfsBufLen = testPayloadLen * 2
|
||||||
|
|
||||||
func TestRecvDataFromRemote(t *testing.T) {
|
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
|
var sessionKey [32]byte
|
||||||
rand.Read(sessionKey[:])
|
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 {
|
for seshType, seshConfig := range seshConfigs {
|
||||||
seshConfig := seshConfig
|
seshConfig := seshConfig
|
||||||
t.Run(seshType, func(t *testing.T) {
|
t.Run(seshType, func(t *testing.T) {
|
||||||
for method, obfuscator := range encryptionMethods {
|
var err error
|
||||||
obfuscator := obfuscator
|
seshConfig.Obfuscator, err = MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||||
t.Run(method, func(t *testing.T) {
|
if err != nil {
|
||||||
seshConfig.Obfuscator = obfuscator
|
t.Fatalf("failed to make obfuscator: %v", err)
|
||||||
|
}
|
||||||
|
t.Run("initial frame", func(t *testing.T) {
|
||||||
sesh := MakeSession(0, seshConfig)
|
sesh := MakeSession(0, seshConfig)
|
||||||
n, err := sesh.Obfs(f, obfsBuf, 0)
|
obfsBuf := make([]byte, obfsBufLen)
|
||||||
if err != nil {
|
f := Frame{
|
||||||
t.Error(err)
|
1,
|
||||||
return
|
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])
|
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stream, err := sesh.Accept()
|
stream, err := sesh.Accept()
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resultPayload := make([]byte, testPayloadLen)
|
resultPayload := make([]byte, testPayloadLen)
|
||||||
_, err = stream.Read(resultPayload)
|
_, err = stream.Read(resultPayload)
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Error(err)
|
|
||||||
return
|
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) {
|
rand.Read(f.Payload)
|
||||||
t.Errorf("Expecting %x, got %x", testPayload, resultPayload)
|
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
|
var sessionKey [32]byte
|
||||||
rand.Read(sessionKey[:])
|
rand.Read(sessionKey[:])
|
||||||
obfuscator, _ := MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
|
||||||
|
|
||||||
seshConfig := seshConfigs["ordered"]
|
seshConfig := seshConfigs["ordered"]
|
||||||
seshConfig.Obfuscator = obfuscator
|
seshConfig.Obfuscator, _ = MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||||
sesh := MakeSession(0, seshConfig)
|
sesh := MakeSession(0, seshConfig)
|
||||||
|
|
||||||
f1 := &Frame{
|
f1 := &Frame{
|
||||||
|
|
@ -107,7 +224,7 @@ func TestRecvDataFromRemote_Closing_InOrder(t *testing.T) {
|
||||||
testPayload,
|
testPayload,
|
||||||
}
|
}
|
||||||
// create stream 1
|
// create stream 1
|
||||||
n, _ := sesh.Obfs(f1, obfsBuf, 0)
|
n, _ := sesh.obfuscate(f1, obfsBuf, 0)
|
||||||
err := sesh.recvDataFromRemote(obfsBuf[:n])
|
err := sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("receiving normal frame for stream 1: %v", err)
|
t.Fatalf("receiving normal frame for stream 1: %v", err)
|
||||||
|
|
@ -129,7 +246,7 @@ func TestRecvDataFromRemote_Closing_InOrder(t *testing.T) {
|
||||||
closingNothing,
|
closingNothing,
|
||||||
testPayload,
|
testPayload,
|
||||||
}
|
}
|
||||||
n, _ = sesh.Obfs(f2, obfsBuf, 0)
|
n, _ = sesh.obfuscate(f2, obfsBuf, 0)
|
||||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("receiving normal frame for stream 2: %v", err)
|
t.Fatalf("receiving normal frame for stream 2: %v", err)
|
||||||
|
|
@ -151,7 +268,7 @@ func TestRecvDataFromRemote_Closing_InOrder(t *testing.T) {
|
||||||
closingStream,
|
closingStream,
|
||||||
testPayload,
|
testPayload,
|
||||||
}
|
}
|
||||||
n, _ = sesh.Obfs(f1CloseStream, obfsBuf, 0)
|
n, _ = sesh.obfuscate(f1CloseStream, obfsBuf, 0)
|
||||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("receiving stream closing frame for stream 1: %v", err)
|
t.Fatalf("receiving stream closing frame for stream 1: %v", err)
|
||||||
|
|
@ -180,7 +297,7 @@ func TestRecvDataFromRemote_Closing_InOrder(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// close stream 1 again
|
// close stream 1 again
|
||||||
n, _ = sesh.Obfs(f1CloseStream, obfsBuf, 0)
|
n, _ = sesh.obfuscate(f1CloseStream, obfsBuf, 0)
|
||||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("receiving stream closing frame for stream 1 %v", err)
|
t.Fatalf("receiving stream closing frame for stream 1 %v", err)
|
||||||
|
|
@ -203,7 +320,7 @@ func TestRecvDataFromRemote_Closing_InOrder(t *testing.T) {
|
||||||
Closing: closingSession,
|
Closing: closingSession,
|
||||||
Payload: testPayload,
|
Payload: testPayload,
|
||||||
}
|
}
|
||||||
n, _ = sesh.Obfs(fCloseSession, obfsBuf, 0)
|
n, _ = sesh.obfuscate(fCloseSession, obfsBuf, 0)
|
||||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("receiving session closing frame: %v", err)
|
t.Fatalf("receiving session closing frame: %v", err)
|
||||||
|
|
@ -233,10 +350,9 @@ func TestRecvDataFromRemote_Closing_OutOfOrder(t *testing.T) {
|
||||||
|
|
||||||
var sessionKey [32]byte
|
var sessionKey [32]byte
|
||||||
rand.Read(sessionKey[:])
|
rand.Read(sessionKey[:])
|
||||||
obfuscator, _ := MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
|
||||||
|
|
||||||
seshConfig := seshConfigs["ordered"]
|
seshConfig := seshConfigs["ordered"]
|
||||||
seshConfig.Obfuscator = obfuscator
|
seshConfig.Obfuscator, _ = MakeObfuscator(EncryptionMethodPlain, sessionKey)
|
||||||
sesh := MakeSession(0, seshConfig)
|
sesh := MakeSession(0, seshConfig)
|
||||||
|
|
||||||
// receive stream 1 closing first
|
// receive stream 1 closing first
|
||||||
|
|
@ -246,7 +362,7 @@ func TestRecvDataFromRemote_Closing_OutOfOrder(t *testing.T) {
|
||||||
closingStream,
|
closingStream,
|
||||||
testPayload,
|
testPayload,
|
||||||
}
|
}
|
||||||
n, _ := sesh.Obfs(f1CloseStream, obfsBuf, 0)
|
n, _ := sesh.obfuscate(f1CloseStream, obfsBuf, 0)
|
||||||
err := sesh.recvDataFromRemote(obfsBuf[:n])
|
err := sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("receiving out of order stream closing frame for stream 1: %v", err)
|
t.Fatalf("receiving out of order stream closing frame for stream 1: %v", err)
|
||||||
|
|
@ -268,7 +384,7 @@ func TestRecvDataFromRemote_Closing_OutOfOrder(t *testing.T) {
|
||||||
closingNothing,
|
closingNothing,
|
||||||
testPayload,
|
testPayload,
|
||||||
}
|
}
|
||||||
n, _ = sesh.Obfs(f1, obfsBuf, 0)
|
n, _ = sesh.obfuscate(f1, obfsBuf, 0)
|
||||||
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
err = sesh.recvDataFromRemote(obfsBuf[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("receiving normal frame for stream 1: %v", err)
|
t.Fatalf("receiving normal frame for stream 1: %v", err)
|
||||||
|
|
@ -330,7 +446,7 @@ func TestParallelStreams(t *testing.T) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(frame *Frame) {
|
go func(frame *Frame) {
|
||||||
obfsBuf := make([]byte, obfsBufLen)
|
obfsBuf := make([]byte, obfsBufLen)
|
||||||
n, _ := sesh.Obfs(frame, obfsBuf, 0)
|
n, _ := sesh.obfuscate(frame, obfsBuf, 0)
|
||||||
obfsBuf = obfsBuf[0:n]
|
obfsBuf = obfsBuf[0:n]
|
||||||
|
|
||||||
err := sesh.recvDataFromRemote(obfsBuf)
|
err := sesh.recvDataFromRemote(obfsBuf)
|
||||||
|
|
@ -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)
|
testPayload := make([]byte, testPayloadLen)
|
||||||
rand.Read(testPayload)
|
rand.Read(testPayload)
|
||||||
f := &Frame{
|
f := &Frame{
|
||||||
|
|
@ -428,25 +544,24 @@ func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
|
||||||
var sessionKey [32]byte
|
var sessionKey [32]byte
|
||||||
rand.Read(sessionKey[:])
|
rand.Read(sessionKey[:])
|
||||||
|
|
||||||
table := map[string]byte{
|
|
||||||
"plain": EncryptionMethodPlain,
|
|
||||||
"aes-gcm": EncryptionMethodAES256GCM,
|
|
||||||
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxIter = 100_000 // run with -benchtime 100000x to avoid index out of bounds panic
|
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
|
ep := ep
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
seshConfig := seshConfigs["ordered"]
|
for seshType, seshConfig := range seshConfigs {
|
||||||
obfuscator, _ := MakeObfuscator(ep, sessionKey)
|
b.Run(seshType, func(b *testing.B) {
|
||||||
seshConfig.Obfuscator = obfuscator
|
seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
|
||||||
sesh := MakeSession(0, seshConfig)
|
sesh := MakeSession(0, seshConfig)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
stream, _ := sesh.Accept()
|
||||||
|
stream.(*Stream).WriteTo(ioutil.Discard)
|
||||||
|
}()
|
||||||
|
|
||||||
binaryFrames := [maxIter][]byte{}
|
binaryFrames := [maxIter][]byte{}
|
||||||
for i := 0; i < maxIter; i++ {
|
for i := 0; i < maxIter; i++ {
|
||||||
obfsBuf := make([]byte, obfsBufLen)
|
obfsBuf := make([]byte, obfsBufLen)
|
||||||
n, _ := sesh.Obfs(f, obfsBuf, 0)
|
n, _ := sesh.obfuscate(f, obfsBuf, 0)
|
||||||
binaryFrames[i] = obfsBuf[:n]
|
binaryFrames[i] = obfsBuf[:n]
|
||||||
f.Seq++
|
f.Seq++
|
||||||
}
|
}
|
||||||
|
|
@ -458,27 +573,21 @@ func BenchmarkRecvDataFromRemote_Ordered(b *testing.B) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMultiStreamWrite(b *testing.B) {
|
func BenchmarkMultiStreamWrite(b *testing.B) {
|
||||||
var sessionKey [32]byte
|
var sessionKey [32]byte
|
||||||
rand.Read(sessionKey[:])
|
rand.Read(sessionKey[:])
|
||||||
|
|
||||||
table := map[string]byte{
|
|
||||||
"plain": EncryptionMethodPlain,
|
|
||||||
"aes-gcm": EncryptionMethodAES256GCM,
|
|
||||||
"chacha20poly1305": EncryptionMethodChaha20Poly1305,
|
|
||||||
}
|
|
||||||
|
|
||||||
testPayload := make([]byte, testPayloadLen)
|
testPayload := make([]byte, testPayloadLen)
|
||||||
|
|
||||||
for name, ep := range table {
|
for name, ep := range encryptionMethods {
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
for seshType, seshConfig := range seshConfigs {
|
for seshType, seshConfig := range seshConfigs {
|
||||||
seshConfig := seshConfig
|
|
||||||
b.Run(seshType, func(b *testing.B) {
|
b.Run(seshType, func(b *testing.B) {
|
||||||
obfuscator, _ := MakeObfuscator(ep, sessionKey)
|
seshConfig.Obfuscator, _ = MakeObfuscator(ep, sessionKey)
|
||||||
seshConfig.Obfuscator = obfuscator
|
|
||||||
sesh := MakeSession(0, seshConfig)
|
sesh := MakeSession(0, seshConfig)
|
||||||
sesh.AddConnection(connutil.Discard())
|
sesh.AddConnection(connutil.Discard())
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
@ -494,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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func (s *Stream) WriteTo(w io.Writer) (int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) obfuscateAndSend(buf []byte, payloadOffsetInBuf int) error {
|
func (s *Stream) obfuscateAndSend(buf []byte, payloadOffsetInBuf int) error {
|
||||||
cipherTextLen, err := s.session.Obfs(&s.writingFrame, buf, payloadOffsetInBuf)
|
cipherTextLen, err := s.session.obfuscate(&s.writingFrame, buf, payloadOffsetInBuf)
|
||||||
s.writingFrame.Seq++
|
s.writingFrame.Seq++
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ func TestStream_Close(t *testing.T) {
|
||||||
writingEnd := common.NewTLSConn(rawWritingEnd)
|
writingEnd := common.NewTLSConn(rawWritingEnd)
|
||||||
|
|
||||||
obfsBuf := make([]byte, 512)
|
obfsBuf := make([]byte, 512)
|
||||||
i, _ := sesh.Obfs(dataFrame, obfsBuf, 0)
|
i, _ := sesh.obfuscate(dataFrame, obfsBuf, 0)
|
||||||
_, err := writingEnd.Write(obfsBuf[:i])
|
_, err := writingEnd.Write(obfsBuf[:i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to write from remote end")
|
t.Error("failed to write from remote end")
|
||||||
|
|
@ -185,7 +185,7 @@ func TestStream_Close(t *testing.T) {
|
||||||
writingEnd := common.NewTLSConn(rawWritingEnd)
|
writingEnd := common.NewTLSConn(rawWritingEnd)
|
||||||
|
|
||||||
obfsBuf := make([]byte, 512)
|
obfsBuf := make([]byte, 512)
|
||||||
i, err := sesh.Obfs(dataFrame, obfsBuf, 0)
|
i, err := sesh.obfuscate(dataFrame, obfsBuf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to obfuscate frame %v", err)
|
t.Errorf("failed to obfuscate frame %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +207,7 @@ func TestStream_Close(t *testing.T) {
|
||||||
testPayload,
|
testPayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
i, err = sesh.Obfs(closingFrame, obfsBuf, 0)
|
i, err = sesh.obfuscate(closingFrame, obfsBuf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to obfuscate frame %v", err)
|
t.Errorf("failed to obfuscate frame %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -223,7 +223,7 @@ func TestStream_Close(t *testing.T) {
|
||||||
testPayload,
|
testPayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
i, err = sesh.Obfs(closingFrameDup, obfsBuf, 0)
|
i, err = sesh.obfuscate(closingFrameDup, obfsBuf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to obfuscate frame %v", err)
|
t.Errorf("failed to obfuscate frame %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -275,7 +275,7 @@ func TestStream_Read(t *testing.T) {
|
||||||
obfsBuf := make([]byte, 512)
|
obfsBuf := make([]byte, 512)
|
||||||
t.Run("Plain read", func(t *testing.T) {
|
t.Run("Plain read", func(t *testing.T) {
|
||||||
f.StreamID = streamID
|
f.StreamID = streamID
|
||||||
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
i, _ := sesh.obfuscate(f, obfsBuf, 0)
|
||||||
streamID++
|
streamID++
|
||||||
writingEnd.Write(obfsBuf[:i])
|
writingEnd.Write(obfsBuf[:i])
|
||||||
stream, err := sesh.Accept()
|
stream, err := sesh.Accept()
|
||||||
|
|
@ -300,7 +300,7 @@ func TestStream_Read(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Nil buf", func(t *testing.T) {
|
t.Run("Nil buf", func(t *testing.T) {
|
||||||
f.StreamID = streamID
|
f.StreamID = streamID
|
||||||
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
i, _ := sesh.obfuscate(f, obfsBuf, 0)
|
||||||
streamID++
|
streamID++
|
||||||
writingEnd.Write(obfsBuf[:i])
|
writingEnd.Write(obfsBuf[:i])
|
||||||
stream, _ := sesh.Accept()
|
stream, _ := sesh.Accept()
|
||||||
|
|
@ -312,7 +312,7 @@ func TestStream_Read(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Read after stream close", func(t *testing.T) {
|
t.Run("Read after stream close", func(t *testing.T) {
|
||||||
f.StreamID = streamID
|
f.StreamID = streamID
|
||||||
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
i, _ := sesh.obfuscate(f, obfsBuf, 0)
|
||||||
streamID++
|
streamID++
|
||||||
writingEnd.Write(obfsBuf[:i])
|
writingEnd.Write(obfsBuf[:i])
|
||||||
stream, _ := sesh.Accept()
|
stream, _ := sesh.Accept()
|
||||||
|
|
@ -337,7 +337,7 @@ func TestStream_Read(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Read after session close", func(t *testing.T) {
|
t.Run("Read after session close", func(t *testing.T) {
|
||||||
f.StreamID = streamID
|
f.StreamID = streamID
|
||||||
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
i, _ := sesh.obfuscate(f, obfsBuf, 0)
|
||||||
streamID++
|
streamID++
|
||||||
writingEnd.Write(obfsBuf[:i])
|
writingEnd.Write(obfsBuf[:i])
|
||||||
stream, _ := sesh.Accept()
|
stream, _ := sesh.Accept()
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ func dispatchConnection(conn net.Conn, sta *State) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
preparedConn, err := finishHandshake(conn, sesh.SessionKey, sta.WorldState.Rand)
|
preparedConn, err := finishHandshake(conn, sesh.GetSessionKey(), sta.WorldState.Rand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue