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  }