github.com/igoogolx/clash@v1.19.8/transport/shadowsocks/core/cipher.go (about) 1 package core 2 3 import ( 4 "crypto/md5" 5 "errors" 6 "net" 7 "sort" 8 "strings" 9 10 "github.com/igoogolx/clash/transport/shadowsocks/shadowaead" 11 "github.com/igoogolx/clash/transport/shadowsocks/shadowstream" 12 ) 13 14 type Cipher interface { 15 StreamConnCipher 16 PacketConnCipher 17 } 18 19 type StreamConnCipher interface { 20 StreamConn(net.Conn) net.Conn 21 } 22 23 type PacketConnCipher interface { 24 PacketConn(net.PacketConn) net.PacketConn 25 } 26 27 // ErrCipherNotSupported occurs when a cipher is not supported (likely because of security concerns). 28 var ErrCipherNotSupported = errors.New("cipher not supported") 29 30 const ( 31 aeadAes128Gcm = "AEAD_AES_128_GCM" 32 aeadAes192Gcm = "AEAD_AES_192_GCM" 33 aeadAes256Gcm = "AEAD_AES_256_GCM" 34 aeadChacha20Poly1305 = "AEAD_CHACHA20_POLY1305" 35 aeadXChacha20Poly1305 = "AEAD_XCHACHA20_POLY1305" 36 ) 37 38 // List of AEAD ciphers: key size in bytes and constructor 39 var aeadList = map[string]struct { 40 KeySize int 41 New func([]byte) (shadowaead.Cipher, error) 42 }{ 43 aeadAes128Gcm: {16, shadowaead.AESGCM}, 44 aeadAes192Gcm: {24, shadowaead.AESGCM}, 45 aeadAes256Gcm: {32, shadowaead.AESGCM}, 46 aeadChacha20Poly1305: {32, shadowaead.Chacha20Poly1305}, 47 aeadXChacha20Poly1305: {32, shadowaead.XChacha20Poly1305}, 48 } 49 50 // List of stream ciphers: key size in bytes and constructor 51 var streamList = map[string]struct { 52 KeySize int 53 New func(key []byte) (shadowstream.Cipher, error) 54 }{ 55 "RC4-MD5": {16, shadowstream.RC4MD5}, 56 "AES-128-CTR": {16, shadowstream.AESCTR}, 57 "AES-192-CTR": {24, shadowstream.AESCTR}, 58 "AES-256-CTR": {32, shadowstream.AESCTR}, 59 "AES-128-CFB": {16, shadowstream.AESCFB}, 60 "AES-192-CFB": {24, shadowstream.AESCFB}, 61 "AES-256-CFB": {32, shadowstream.AESCFB}, 62 "CHACHA20-IETF": {32, shadowstream.Chacha20IETF}, 63 "XCHACHA20": {32, shadowstream.Xchacha20}, 64 } 65 66 // ListCipher returns a list of available cipher names sorted alphabetically. 67 func ListCipher() []string { 68 var l []string 69 for k := range aeadList { 70 l = append(l, k) 71 } 72 for k := range streamList { 73 l = append(l, k) 74 } 75 sort.Strings(l) 76 return l 77 } 78 79 // PickCipher returns a Cipher of the given name. Derive key from password if given key is empty. 80 func PickCipher(name string, key []byte, password string) (Cipher, error) { 81 name = strings.ToUpper(name) 82 83 switch name { 84 case "DUMMY": 85 return &dummy{}, nil 86 case "CHACHA20-IETF-POLY1305": 87 name = aeadChacha20Poly1305 88 case "XCHACHA20-IETF-POLY1305": 89 name = aeadXChacha20Poly1305 90 case "AES-128-GCM": 91 name = aeadAes128Gcm 92 case "AES-192-GCM": 93 name = aeadAes192Gcm 94 case "AES-256-GCM": 95 name = aeadAes256Gcm 96 } 97 98 if choice, ok := aeadList[name]; ok { 99 if len(key) == 0 { 100 key = Kdf(password, choice.KeySize) 101 } 102 if len(key) != choice.KeySize { 103 return nil, shadowaead.KeySizeError(choice.KeySize) 104 } 105 aead, err := choice.New(key) 106 return &AeadCipher{Cipher: aead, Key: key}, err 107 } 108 109 if choice, ok := streamList[name]; ok { 110 if len(key) == 0 { 111 key = Kdf(password, choice.KeySize) 112 } 113 if len(key) != choice.KeySize { 114 return nil, shadowstream.KeySizeError(choice.KeySize) 115 } 116 ciph, err := choice.New(key) 117 return &StreamCipher{Cipher: ciph, Key: key}, err 118 } 119 120 return nil, ErrCipherNotSupported 121 } 122 123 type AeadCipher struct { 124 shadowaead.Cipher 125 126 Key []byte 127 } 128 129 func (aead *AeadCipher) StreamConn(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) } 130 func (aead *AeadCipher) PacketConn(c net.PacketConn) net.PacketConn { 131 return shadowaead.NewPacketConn(c, aead) 132 } 133 134 type StreamCipher struct { 135 shadowstream.Cipher 136 137 Key []byte 138 } 139 140 func (ciph *StreamCipher) StreamConn(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) } 141 func (ciph *StreamCipher) PacketConn(c net.PacketConn) net.PacketConn { 142 return shadowstream.NewPacketConn(c, ciph) 143 } 144 145 // dummy cipher does not encrypt 146 147 type dummy struct{} 148 149 func (dummy) StreamConn(c net.Conn) net.Conn { return c } 150 func (dummy) PacketConn(c net.PacketConn) net.PacketConn { return c } 151 152 // key-derivation function from original Shadowsocks 153 func Kdf(password string, keyLen int) []byte { 154 var b, prev []byte 155 h := md5.New() 156 for len(b) < keyLen { 157 h.Write(prev) 158 h.Write([]byte(password)) 159 b = h.Sum(b) 160 prev = b[len(b)-h.Size():] 161 h.Reset() 162 } 163 return b[:keyLen] 164 }