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