github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/proxy/shadowsocks/config.go (about)

     1  package shadowsocks
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/aes"
     6  	"crypto/cipher"
     7  	"crypto/md5"
     8  	"crypto/sha1"
     9  	"io"
    10  	"strings"
    11  
    12  	"golang.org/x/crypto/chacha20poly1305"
    13  	"golang.org/x/crypto/hkdf"
    14  
    15  	"github.com/v2fly/v2ray-core/v5/common"
    16  	"github.com/v2fly/v2ray-core/v5/common/antireplay"
    17  	"github.com/v2fly/v2ray-core/v5/common/buf"
    18  	"github.com/v2fly/v2ray-core/v5/common/crypto"
    19  	"github.com/v2fly/v2ray-core/v5/common/protocol"
    20  )
    21  
    22  // MemoryAccount is an account type converted from Account.
    23  type MemoryAccount struct {
    24  	Cipher Cipher
    25  	Key    []byte
    26  
    27  	replayFilter antireplay.GeneralizedReplayFilter
    28  
    29  	ReducedIVEntropy bool
    30  }
    31  
    32  // Equals implements protocol.Account.Equals().
    33  func (a *MemoryAccount) Equals(another protocol.Account) bool {
    34  	if account, ok := another.(*MemoryAccount); ok {
    35  		return bytes.Equal(a.Key, account.Key)
    36  	}
    37  	return false
    38  }
    39  
    40  func (a *MemoryAccount) CheckIV(iv []byte) error {
    41  	if a.replayFilter == nil {
    42  		return nil
    43  	}
    44  	if a.replayFilter.Check(iv) {
    45  		return nil
    46  	}
    47  	return newError("IV is not unique")
    48  }
    49  
    50  func createAesGcm(key []byte) cipher.AEAD {
    51  	block, err := aes.NewCipher(key)
    52  	common.Must(err)
    53  	gcm, err := cipher.NewGCM(block)
    54  	common.Must(err)
    55  	return gcm
    56  }
    57  
    58  func createChaCha20Poly1305(key []byte) cipher.AEAD {
    59  	ChaChaPoly1305, err := chacha20poly1305.New(key)
    60  	common.Must(err)
    61  	return ChaChaPoly1305
    62  }
    63  
    64  func (a *Account) getCipher() (Cipher, error) {
    65  	switch a.CipherType {
    66  	case CipherType_AES_128_GCM:
    67  		return &AEADCipher{
    68  			KeyBytes:        16,
    69  			IVBytes:         16,
    70  			AEADAuthCreator: createAesGcm,
    71  		}, nil
    72  	case CipherType_AES_256_GCM:
    73  		return &AEADCipher{
    74  			KeyBytes:        32,
    75  			IVBytes:         32,
    76  			AEADAuthCreator: createAesGcm,
    77  		}, nil
    78  	case CipherType_CHACHA20_POLY1305:
    79  		return &AEADCipher{
    80  			KeyBytes:        32,
    81  			IVBytes:         32,
    82  			AEADAuthCreator: createChaCha20Poly1305,
    83  		}, nil
    84  	case CipherType_NONE:
    85  		return NoneCipher{}, nil
    86  	default:
    87  		return nil, newError("Unsupported cipher.")
    88  	}
    89  }
    90  
    91  // AsAccount implements protocol.AsAccount.
    92  func (a *Account) AsAccount() (protocol.Account, error) {
    93  	Cipher, err := a.getCipher()
    94  	if err != nil {
    95  		return nil, newError("failed to get cipher").Base(err)
    96  	}
    97  	return &MemoryAccount{
    98  		Cipher: Cipher,
    99  		Key:    passwordToCipherKey([]byte(a.Password), Cipher.KeySize()),
   100  		replayFilter: func() antireplay.GeneralizedReplayFilter {
   101  			if a.IvCheck {
   102  				return antireplay.NewBloomRing()
   103  			}
   104  			return nil
   105  		}(),
   106  		ReducedIVEntropy: a.ExperimentReducedIvHeadEntropy,
   107  	}, nil
   108  }
   109  
   110  // Cipher is an interface for all Shadowsocks ciphers.
   111  type Cipher interface {
   112  	KeySize() int32
   113  	IVSize() int32
   114  	NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error)
   115  	NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error)
   116  	IsAEAD() bool
   117  	EncodePacket(key []byte, b *buf.Buffer) error
   118  	DecodePacket(key []byte, b *buf.Buffer) error
   119  }
   120  
   121  type AEADCipher struct {
   122  	KeyBytes        int32
   123  	IVBytes         int32
   124  	AEADAuthCreator func(key []byte) cipher.AEAD
   125  }
   126  
   127  func (*AEADCipher) IsAEAD() bool {
   128  	return true
   129  }
   130  
   131  func (c *AEADCipher) KeySize() int32 {
   132  	return c.KeyBytes
   133  }
   134  
   135  func (c *AEADCipher) IVSize() int32 {
   136  	return c.IVBytes
   137  }
   138  
   139  func (c *AEADCipher) createAuthenticator(key []byte, iv []byte) *crypto.AEADAuthenticator {
   140  	nonce := crypto.GenerateInitialAEADNonce()
   141  	subkey := make([]byte, c.KeyBytes)
   142  	hkdfSHA1(key, iv, subkey)
   143  	return &crypto.AEADAuthenticator{
   144  		AEAD:           c.AEADAuthCreator(subkey),
   145  		NonceGenerator: nonce,
   146  	}
   147  }
   148  
   149  func (c *AEADCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
   150  	auth := c.createAuthenticator(key, iv)
   151  	return crypto.NewAuthenticationWriter(auth, &crypto.AEADChunkSizeParser{
   152  		Auth: auth,
   153  	}, writer, protocol.TransferTypeStream, nil), nil
   154  }
   155  
   156  func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
   157  	auth := c.createAuthenticator(key, iv)
   158  	return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
   159  		Auth: auth,
   160  	}, reader, protocol.TransferTypeStream, nil), nil
   161  }
   162  
   163  func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error {
   164  	ivLen := c.IVSize()
   165  	payloadLen := b.Len()
   166  	auth := c.createAuthenticator(key, b.BytesTo(ivLen))
   167  
   168  	b.Extend(int32(auth.Overhead()))
   169  	_, err := auth.Seal(b.BytesTo(ivLen), b.BytesRange(ivLen, payloadLen))
   170  	return err
   171  }
   172  
   173  func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error {
   174  	if b.Len() <= c.IVSize() {
   175  		return newError("insufficient data: ", b.Len())
   176  	}
   177  	ivLen := c.IVSize()
   178  	payloadLen := b.Len()
   179  	auth := c.createAuthenticator(key, b.BytesTo(ivLen))
   180  
   181  	bbb, err := auth.Open(b.BytesTo(ivLen), b.BytesRange(ivLen, payloadLen))
   182  	if err != nil {
   183  		return err
   184  	}
   185  	b.Resize(ivLen, int32(len(bbb)))
   186  	return nil
   187  }
   188  
   189  type NoneCipher struct{}
   190  
   191  func (NoneCipher) KeySize() int32 { return 0 }
   192  func (NoneCipher) IVSize() int32  { return 0 }
   193  func (NoneCipher) IsAEAD() bool {
   194  	return false
   195  }
   196  
   197  func (NoneCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
   198  	return buf.NewReader(reader), nil
   199  }
   200  
   201  func (NoneCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
   202  	return buf.NewWriter(writer), nil
   203  }
   204  
   205  func (NoneCipher) EncodePacket(key []byte, b *buf.Buffer) error {
   206  	return nil
   207  }
   208  
   209  func (NoneCipher) DecodePacket(key []byte, b *buf.Buffer) error {
   210  	return nil
   211  }
   212  
   213  func CipherFromString(c string) CipherType {
   214  	switch strings.ToLower(c) {
   215  	case "aes-128-gcm", "aes_128_gcm", "aead_aes_128_gcm":
   216  		return CipherType_AES_128_GCM
   217  	case "aes-256-gcm", "aes_256_gcm", "aead_aes_256_gcm":
   218  		return CipherType_AES_256_GCM
   219  	case "chacha20-poly1305", "chacha20_poly1305", "aead_chacha20_poly1305", "chacha20-ietf-poly1305":
   220  		return CipherType_CHACHA20_POLY1305
   221  	case "none", "plain":
   222  		return CipherType_NONE
   223  	default:
   224  		return CipherType_UNKNOWN
   225  	}
   226  }
   227  
   228  func passwordToCipherKey(password []byte, keySize int32) []byte {
   229  	key := make([]byte, 0, keySize)
   230  
   231  	md5Sum := md5.Sum(password)
   232  	key = append(key, md5Sum[:]...)
   233  
   234  	for int32(len(key)) < keySize {
   235  		md5Hash := md5.New()
   236  		common.Must2(md5Hash.Write(md5Sum[:]))
   237  		common.Must2(md5Hash.Write(password))
   238  		md5Hash.Sum(md5Sum[:0])
   239  
   240  		key = append(key, md5Sum[:]...)
   241  	}
   242  	return key
   243  }
   244  
   245  func hkdfSHA1(secret, salt, outKey []byte) {
   246  	r := hkdf.New(sha1.New, secret, salt, []byte("ss-subkey"))
   247  	common.Must2(io.ReadFull(r, outKey))
   248  }