diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index 84b461a3..5d264b74 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -78,18 +78,6 @@ func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { return DialWithRawAddr(ra, server, cipher) } -func (c *Conn) GetIv() (iv []byte) { - iv = make([]byte, len(c.iv)) - copy(iv, c.iv) - return -} - -func (c *Conn) GetKey() (key []byte) { - key = make([]byte, len(c.key)) - copy(key, c.key) - return -} - func (c *Conn) Read(b []byte) (n int, err error) { if c.dec == nil { iv := make([]byte, c.info.ivLen) @@ -99,9 +87,6 @@ func (c *Conn) Read(b []byte) (n int, err error) { if err = c.initDecrypt(iv); err != nil { return } - if len(c.iv) == 0 { - c.iv = iv - } } cipherData := c.readBuf diff --git a/shadowsocks/conn_test.go b/shadowsocks/conn_test.go new file mode 100644 index 00000000..b24aed75 --- /dev/null +++ b/shadowsocks/conn_test.go @@ -0,0 +1,89 @@ +package shadowsocks + +import ( + "bytes" + "io" + "net" + "testing" +) + +func mustNewCipher(method string) *Cipher { + const testPassword = "password" + cipher, err := NewCipher(method, testPassword) + if err != nil { + panic(err) + } + return cipher +} + +type transcriptConn struct { + net.Conn + ReadTranscript []byte +} + +func (conn *transcriptConn) Read(p []byte) (int, error) { + n, err := conn.Conn.Read(p) + conn.ReadTranscript = append(conn.ReadTranscript, p[:n]...) + return n, err +} + +func connIVs(method string) (clientIV, serverIV []byte, err error) { + // underlying network connection + clientConn, serverConn := net.Pipe() + // make a transcript of bytes at the network level + clientTranscriptConn := &transcriptConn{Conn: clientConn} + serverTranscriptConn := &transcriptConn{Conn: serverConn} + // connection at the ShadowSocks level + clientSSConn := NewConn(clientTranscriptConn, mustNewCipher(method)) + serverSSConn := NewConn(serverTranscriptConn, mustNewCipher(method)) + + clientToServerData := []byte("clientToServerData") + serverToClientData := []byte("serverToClientData") + + go func() { + defer serverSSConn.Close() + buf := make([]byte, len(clientToServerData)) + // read the client IV + _, err := io.ReadFull(serverSSConn, buf) + if err != nil { + return + } + // send the server IV + _, err = serverSSConn.Write(serverToClientData) + if err != nil { + return + } + }() + + // send the client IV + _, err = clientSSConn.Write(clientToServerData) + if err != nil { + return + } + // read the server IV + buf := make([]byte, len(serverToClientData)) + _, err = io.ReadFull(clientSSConn, buf) + if err != nil { + return + } + + // pull the IVs out of the network transcripts + clientIV = serverTranscriptConn.ReadTranscript[:clientSSConn.Cipher.info.ivLen] + serverIV = clientTranscriptConn.ReadTranscript[:serverSSConn.Cipher.info.ivLen] + + return +} + +func TestIndependentIVs(t *testing.T) { + for method := range cipherMethod { + clientIV, serverIV, err := connIVs(method) + if err != nil { + t.Errorf("%s connection error: %s", method, err) + continue + } + if bytes.Equal(clientIV, serverIV) { + t.Errorf("%s equal client and server IVs", method) + continue + } + } +} diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index eee4c366..60790d8a 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -188,7 +188,6 @@ type Cipher struct { dec cipher.Stream key []byte info *cipherInfo - iv []byte } // NewCipher creates a cipher that can be used in Dial() etc. @@ -215,14 +214,9 @@ func NewCipher(method, password string) (c *Cipher, err error) { // Initializes the block cipher with CFB mode, returns IV. func (c *Cipher) initEncrypt() (iv []byte, err error) { - if c.iv == nil { - iv = make([]byte, c.info.ivLen) - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - c.iv = iv - } else { - iv = c.iv + iv = make([]byte, c.info.ivLen) + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err } c.enc, err = c.info.newStream(c.key, iv, Encrypt) return