github.com/metacubex/mihomo@v1.18.5/transport/shadowsocks/shadowaead/cipher.go (about)

     1  package shadowaead
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/sha1"
     7  	"io"
     8  	"strconv"
     9  
    10  	"golang.org/x/crypto/chacha20poly1305"
    11  	"golang.org/x/crypto/hkdf"
    12  )
    13  
    14  type Cipher interface {
    15  	KeySize() int
    16  	SaltSize() int
    17  	Encrypter(salt []byte) (cipher.AEAD, error)
    18  	Decrypter(salt []byte) (cipher.AEAD, error)
    19  }
    20  
    21  type KeySizeError int
    22  
    23  func (e KeySizeError) Error() string {
    24  	return "key size error: need " + strconv.Itoa(int(e)) + " bytes"
    25  }
    26  
    27  func hkdfSHA1(secret, salt, info, outkey []byte) {
    28  	r := hkdf.New(sha1.New, secret, salt, info)
    29  	if _, err := io.ReadFull(r, outkey); err != nil {
    30  		panic(err) // should never happen
    31  	}
    32  }
    33  
    34  type metaCipher struct {
    35  	psk      []byte
    36  	makeAEAD func(key []byte) (cipher.AEAD, error)
    37  }
    38  
    39  func (a *metaCipher) KeySize() int { return len(a.psk) }
    40  func (a *metaCipher) SaltSize() int {
    41  	if ks := a.KeySize(); ks > 16 {
    42  		return ks
    43  	}
    44  	return 16
    45  }
    46  
    47  func (a *metaCipher) Encrypter(salt []byte) (cipher.AEAD, error) {
    48  	subkey := make([]byte, a.KeySize())
    49  	hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey)
    50  	return a.makeAEAD(subkey)
    51  }
    52  
    53  func (a *metaCipher) Decrypter(salt []byte) (cipher.AEAD, error) {
    54  	subkey := make([]byte, a.KeySize())
    55  	hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey)
    56  	return a.makeAEAD(subkey)
    57  }
    58  
    59  func aesGCM(key []byte) (cipher.AEAD, error) {
    60  	blk, err := aes.NewCipher(key)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	return cipher.NewGCM(blk)
    65  }
    66  
    67  // AESGCM creates a new Cipher with a pre-shared key. len(psk) must be
    68  // one of 16, 24, or 32 to select AES-128/196/256-GCM.
    69  func AESGCM(psk []byte) (Cipher, error) {
    70  	switch l := len(psk); l {
    71  	case 16, 24, 32: // AES 128/196/256
    72  	default:
    73  		return nil, aes.KeySizeError(l)
    74  	}
    75  	return &metaCipher{psk: psk, makeAEAD: aesGCM}, nil
    76  }
    77  
    78  // Chacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk)
    79  // must be 32.
    80  func Chacha20Poly1305(psk []byte) (Cipher, error) {
    81  	if len(psk) != chacha20poly1305.KeySize {
    82  		return nil, KeySizeError(chacha20poly1305.KeySize)
    83  	}
    84  	return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.New}, nil
    85  }
    86  
    87  // XChacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk)
    88  // must be 32.
    89  func XChacha20Poly1305(psk []byte) (Cipher, error) {
    90  	if len(psk) != chacha20poly1305.KeySize {
    91  		return nil, KeySizeError(chacha20poly1305.KeySize)
    92  	}
    93  	return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.NewX}, nil
    94  }