diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index 6e51077..03c555e 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -4,10 +4,12 @@ package main import ( "crypto/aes" + "crypto/cipher" "encoding/base64" "encoding/binary" "flag" "fmt" + "golang.org/x/crypto/chacha20poly1305" "io" "log" "math/rand" @@ -100,13 +102,36 @@ func makeSession(sta *client.State) *mux.Session { } sta.UpdateIntervalKeys() - _, tthKey := sta.GetIntervalKeys() + + var payloadCipher cipher.AEAD + var err error + switch sta.EncryptionMethod { + case 0x00: + payloadCipher = nil + case 0x01: + c, err := aes.NewCipher(tthKey) + if err != nil { + log.Fatal(err) + } + payloadCipher, err = cipher.NewGCM(c) + if err != nil { + log.Fatal(err) + } + case 0x02: + payloadCipher, err = chacha20poly1305.New(tthKey) + if err != nil { + log.Fatal(err) + } + default: + log.Fatal("Unknown encryption method") + } + headerCipher, err := aes.NewCipher(tthKey) if err != nil { log.Fatal(err) } - sesh := mux.MakeSession(sta.SessionID, mux.UNLIMITED_VALVE, mux.MakeObfs(headerCipher, sta.Cipher), mux.MakeDeobfs(headerCipher, sta.Cipher), util.ReadTLS) + sesh := mux.MakeSession(sta.SessionID, mux.UNLIMITED_VALVE, mux.MakeObfs(headerCipher, payloadCipher), mux.MakeDeobfs(headerCipher, payloadCipher), util.ReadTLS) var wg sync.WaitGroup for i := 0; i < sta.NumConn; i++ { diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index 49e3d30..de473f0 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -3,9 +3,11 @@ package main import ( "bytes" "crypto/aes" + "crypto/cipher" "encoding/base64" "flag" "fmt" + "golang.org/x/crypto/chacha20poly1305" "io" "log" "net" @@ -86,19 +88,25 @@ func dispatchConnection(conn net.Conn, sta *server.State) { return } - var crypto mux.Crypto + var payloadCipher cipher.AEAD switch encryptionMethod { case 0x00: - crypto = &mux.Plain{} + payloadCipher = nil case 0x01: - crypto, err = mux.MakeAESGCMCipher(UID) + c, err := aes.NewCipher(tthKey) + if err != nil { + log.Println(err) + goWeb(data) + return + } + payloadCipher, err = cipher.NewGCM(c) if err != nil { log.Println(err) goWeb(data) return } case 0x02: - crypto, err = mux.MakeCPCipher(UID) + payloadCipher, err = chacha20poly1305.New(tthKey) if err != nil { log.Println(err) goWeb(data) @@ -117,8 +125,8 @@ func dispatchConnection(conn net.Conn, sta *server.State) { return } - obfs := mux.MakeObfs(headerCipher, crypto) - deobfs := mux.MakeDeobfs(headerCipher, crypto) + obfs := mux.MakeObfs(headerCipher, payloadCipher) + deobfs := mux.MakeDeobfs(headerCipher, payloadCipher) finishHandshake := func() error { reply := server.ComposeReply(ch) diff --git a/internal/client/state.go b/internal/client/state.go index c9fa0ab..813ee1d 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -12,7 +12,6 @@ import ( "time" "github.com/cbeuw/Cloak/internal/ecdh" - mux "github.com/cbeuw/Cloak/internal/multiplex" ) type rawConfig struct { @@ -51,7 +50,6 @@ type State struct { ProxyMethod string EncryptionMethod byte - Cipher mux.Crypto TicketTimeHint int ServerName string BrowserSig string @@ -139,19 +137,10 @@ func (sta *State) ParseConfig(conf string) (err error) { switch preParse.EncryptionMethod { case "plain": sta.EncryptionMethod = 0x00 - sta.Cipher = &mux.Plain{} case "aes-gcm": sta.EncryptionMethod = 0x01 - sta.Cipher, err = mux.MakeAESGCMCipher(sta.UID) - if err != nil { - return err - } case "chacha20-poly1305": sta.EncryptionMethod = 0x02 - sta.Cipher, err = mux.MakeCPCipher(sta.UID) - if err != nil { - return err - } default: return errors.New("Unknown encryption method") } diff --git a/internal/multiplex/crypto.go b/internal/multiplex/crypto.go deleted file mode 100644 index 8f4c9e3..0000000 --- a/internal/multiplex/crypto.go +++ /dev/null @@ -1,85 +0,0 @@ -package multiplex - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "golang.org/x/crypto/chacha20poly1305" -) - -type Crypto interface { - encrypt([]byte, []byte) ([]byte, error) - decrypt([]byte, []byte) ([]byte, error) -} - -type Plain struct{} - -func (p *Plain) encrypt(plaintext []byte, nonce []byte) ([]byte, error) { - salt := make([]byte, 16) - rand.Read(salt) - return append(plaintext, salt...), nil -} - -func (p *Plain) decrypt(buf []byte, nonce []byte) ([]byte, error) { - return buf[:len(buf)-16], nil -} - -type AESGCM struct { - cipher cipher.AEAD -} - -func MakeAESGCMCipher(key []byte) (*AESGCM, error) { - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - g, err := cipher.NewGCM(c) - if err != nil { - return nil, err - } - ret := AESGCM{ - g, - } - return &ret, nil -} - -func (a *AESGCM) encrypt(plaintext []byte, nonce []byte) ([]byte, error) { - ciphertext := a.cipher.Seal(nil, nonce, plaintext, nil) - return ciphertext, nil -} - -func (a *AESGCM) decrypt(ciphertext []byte, nonce []byte) ([]byte, error) { - plain, err := a.cipher.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - return plain, nil -} - -type C20P1305 struct { - cipher cipher.AEAD -} - -func MakeCPCipher(key []byte) (*C20P1305, error) { - c, err := chacha20poly1305.New(key) - if err != nil { - return nil, err - } - ret := C20P1305{ - c, - } - return &ret, nil -} - -func (c *C20P1305) encrypt(plaintext []byte, nonce []byte) ([]byte, error) { - ciphertext := c.cipher.Seal(nil, nonce, plaintext, nil) - return ciphertext, nil -} - -func (c *C20P1305) decrypt(ciphertext []byte, nonce []byte) ([]byte, error) { - plain, err := c.cipher.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - return plain, nil -} diff --git a/internal/multiplex/obfs.go b/internal/multiplex/obfs.go index c5e2b9d..49cbca5 100644 --- a/internal/multiplex/obfs.go +++ b/internal/multiplex/obfs.go @@ -15,7 +15,7 @@ var putU32 = binary.BigEndian.PutUint32 const HEADER_LEN = 12 -func MakeObfs(headerCipher cipher.Block, algo Crypto) Obfser { +func MakeObfs(headerCipher cipher.Block, payloadCipher cipher.AEAD) Obfser { obfs := func(f *Frame) ([]byte, error) { ret := make([]byte, 5+HEADER_LEN+len(f.Payload)+16) recordLayer := ret[0:5] @@ -28,11 +28,13 @@ func MakeObfs(headerCipher cipher.Block, algo Crypto) Obfser { header[8] = f.Closing rand.Read(header[9:12]) - ciphertext, err := algo.encrypt(f.Payload, header) - if err != nil { - return nil, err + if payloadCipher == nil { + copy(encryptedPayload, f.Payload) + rand.Read(encryptedPayload[len(encryptedPayload)-16:]) + } else { + ciphertext := payloadCipher.Seal(nil, header, f.Payload, nil) + copy(encryptedPayload, ciphertext) } - copy(encryptedPayload, ciphertext) iv := encryptedPayload[len(encryptedPayload)-16:] cipher.NewCTR(headerCipher, iv).XORKeyStream(header, header) @@ -48,7 +50,7 @@ func MakeObfs(headerCipher cipher.Block, algo Crypto) Obfser { return obfs } -func MakeDeobfs(headerCipher cipher.Block, algo Crypto) Deobfser { +func MakeDeobfs(headerCipher cipher.Block, payloadCipher cipher.AEAD) Deobfser { deobfs := func(in []byte) (*Frame, error) { if len(in) < 5+HEADER_LEN+16 { return nil, errors.New("Input cannot be shorter than 33 bytes") @@ -65,13 +67,17 @@ func MakeDeobfs(headerCipher cipher.Block, algo Crypto) Deobfser { seq := u32(header[4:8]) closing := header[8] - decryptedPayload, err := algo.decrypt(payload, header) - if err != nil { - return nil, err - } + outputPayload := make([]byte, len(payload)-16) - outputPayload := make([]byte, len(decryptedPayload)) - copy(outputPayload, decryptedPayload) + if payloadCipher == nil { + copy(outputPayload, payload) + } else { + plaintext, err := payloadCipher.Open(nil, header, payload, nil) + if err != nil { + return nil, err + } + copy(outputPayload, plaintext) + } ret := &Frame{ StreamID: streamID,