forked from shadowsocks/go-shadowsocks2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cipher.go
124 lines (109 loc) · 3.56 KB
/
cipher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package main
import (
"crypto/cipher"
"crypto/md5"
"errors"
"net"
"sort"
"strings"
sscipher "github.com/shadowsocks/go-shadowsocks2/cipher"
"github.com/shadowsocks/go-shadowsocks2/core"
"github.com/shadowsocks/go-shadowsocks2/shadowaead"
"github.com/shadowsocks/go-shadowsocks2/shadowstream"
)
var errCipherNotSupported = errors.New("ciper not supported")
// List of AEAD ciphers: key size in bytes and constructor
var aeadList = map[string]struct {
KeySize int
New func(key []byte) (cipher.AEAD, error)
}{
"aes-128-gcm": {16, sscipher.AESGCM},
"aes-192-gcm": {24, sscipher.AESGCM},
"aes-256-gcm": {32, sscipher.AESGCM},
"aes-128-gcm-16": {16, sscipher.AESGCM16},
"aes-192-gcm-16": {24, sscipher.AESGCM16},
"aes-256-gcm-16": {32, sscipher.AESGCM16},
"chacha20-ietf-poly1305": {32, sscipher.Chacha20IETFPoly1305},
}
// List of stream ciphers: key size in bytes and constructor
var streamList = map[string]struct {
KeySize int
New func(key []byte) (shadowstream.Cipher, error)
}{
"aes-128-ctr": {16, sscipher.AESCTR},
"aes-192-ctr": {24, sscipher.AESCTR},
"aes-256-ctr": {32, sscipher.AESCTR},
"aes-128-cfb": {16, sscipher.AESCFB},
"aes-192-cfb": {24, sscipher.AESCFB},
"aes-256-cfb": {32, sscipher.AESCFB},
"chacha20-ietf": {32, sscipher.Chacha20IETF},
"rc4-md5": {16, sscipher.RC4MD5},
}
// listCipher returns a list of available cipher names sorted alphabetically.
func listCipher() []string {
var l []string
for k := range aeadList {
l = append(l, k)
}
for k := range streamList {
l = append(l, k)
}
sort.Strings(l)
return l
}
// derive key from password if given key is empty
func pickCipher(name string, key []byte, password string) (core.StreamConnCipher, core.PacketConnCipher, error) {
name = strings.ToLower(name)
if name == "dummy" {
return dummyStream(), dummyPacket(), nil
}
if choice, ok := aeadList[name]; ok {
if len(key) == 0 {
key = kdf(password, choice.KeySize)
}
if len(key) != choice.KeySize {
return nil, nil, sscipher.KeySizeError(choice.KeySize)
}
aead, err := choice.New(key)
return aeadStream(aead), aeadPacket(aead), err
}
if choice, ok := streamList[name]; ok {
if len(key) == 0 {
key = kdf(password, choice.KeySize)
}
if len(key) != choice.KeySize {
return nil, nil, sscipher.KeySizeError(choice.KeySize)
}
ciph, err := choice.New(key)
return streamStream(ciph), streamPacket(ciph), err
}
return nil, nil, errCipherNotSupported
}
func aeadStream(aead cipher.AEAD) core.StreamConnCipher {
return func(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) }
}
func aeadPacket(aead cipher.AEAD) core.PacketConnCipher {
return func(c net.PacketConn) net.PacketConn { return shadowaead.NewPacketConn(c, aead) }
}
func streamStream(ciph shadowstream.Cipher) core.StreamConnCipher {
return func(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) }
}
func streamPacket(ciph shadowstream.Cipher) core.PacketConnCipher {
return func(c net.PacketConn) net.PacketConn { return shadowstream.NewPacketConn(c, ciph) }
}
// dummy cipher does not encrypt
func dummyStream() core.StreamConnCipher { return func(c net.Conn) net.Conn { return c } }
func dummyPacket() core.PacketConnCipher { return func(c net.PacketConn) net.PacketConn { return c } }
// key-derivation function from original Shadowsocks
func kdf(password string, keyLen int) []byte {
var b, prev []byte
h := md5.New()
for len(b) < keyLen {
h.Write(prev)
h.Write([]byte(password))
b = h.Sum(b)
prev = b[len(b)-h.Size():]
h.Reset()
}
return b[:keyLen]
}