Skip to content

Commit 264ddf9

Browse files
authored
Merge pull request #92 from Jigsaw-Code/fortuna-key
Fix key derivation
2 parents 8269318 + b1b2572 commit 264ddf9

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

shadowsocks/cipher.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package shadowsocks
1717
import (
1818
"crypto/aes"
1919
"crypto/cipher"
20+
"crypto/md5"
2021
"crypto/sha1"
2122
"fmt"
2223
"io"
@@ -115,13 +116,28 @@ func (c *Cipher) NewAEAD(salt []byte) (cipher.AEAD, error) {
115116
return c.aead.newInstance(sessionKey)
116117
}
117118

119+
// Function definition at https://www.openssl.org/docs/manmaster/man3/EVP_BytesToKey.html
120+
func simpleEVPBytesToKey(data []byte, keyLen int) []byte {
121+
var derived, di []byte
122+
h := md5.New()
123+
for len(derived) < keyLen {
124+
h.Write(di)
125+
h.Write(data)
126+
derived = h.Sum(derived)
127+
di = derived[len(derived)-h.Size():]
128+
h.Reset()
129+
}
130+
return derived[:keyLen]
131+
}
132+
118133
// NewCipher creates a Cipher given a cipher name and a secret
119134
func NewCipher(cipherName string, secretText string) (*Cipher, error) {
120-
secret := []byte(secretText)
121135
aeadSpec, err := getAEADSpec(cipherName)
122136
if err != nil {
123137
return nil, err
124138
}
139+
// Key derivation as per https://shadowsocks.org/en/spec/AEAD-Ciphers.html
140+
secret := simpleEVPBytesToKey([]byte(secretText), aeadSpec.keySize)
125141
return &Cipher{*aeadSpec, secret}, nil
126142
}
127143

shadowsocks/compatibility_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2020 Jigsaw Operations LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package shadowsocks
16+
17+
import (
18+
"io"
19+
"net"
20+
"sync"
21+
"testing"
22+
23+
"github.com/shadowsocks/go-shadowsocks2/core"
24+
"github.com/shadowsocks/go-shadowsocks2/shadowaead"
25+
"github.com/stretchr/testify/require"
26+
)
27+
28+
func TestCompatibility(t *testing.T) {
29+
cipherName := "chacha20-ietf-poly1305"
30+
secret := "secret"
31+
toRight := "payload1"
32+
fromRight := "payload2"
33+
left, right := net.Pipe()
34+
35+
var wait sync.WaitGroup
36+
wait.Add(1)
37+
go func() {
38+
cipher, err := NewCipher(cipherName, secret)
39+
require.Nil(t, err, "NewCipher failed: %v", err)
40+
ssWriter := NewShadowsocksWriter(left, cipher)
41+
ssWriter.Write([]byte(toRight))
42+
43+
ssReader := NewShadowsocksReader(left, cipher)
44+
output := make([]byte, len(fromRight))
45+
_, err = ssReader.Read(output)
46+
require.Nil(t, err, "Read failed: %v", err)
47+
require.Equal(t, fromRight, string(output))
48+
left.Close()
49+
wait.Done()
50+
}()
51+
52+
otherCipher, err := core.PickCipher(cipherName, []byte{}, secret)
53+
require.Nil(t, err)
54+
conn := shadowaead.NewConn(right, otherCipher.(shadowaead.Cipher))
55+
output := make([]byte, len(toRight))
56+
_, err = io.ReadFull(conn, output)
57+
require.Nil(t, err)
58+
require.Equal(t, toRight, string(output))
59+
60+
_, err = conn.Write([]byte(fromRight))
61+
require.Nil(t, err, "Write failed: %v", err)
62+
63+
conn.Close()
64+
wait.Wait()
65+
}

0 commit comments

Comments
 (0)