github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/utils/ss/encrypt.go (about)

     1  package ss
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/des"
     7  	"crypto/md5"
     8  	"crypto/rand"
     9  	"crypto/rc4"
    10  	"encoding/binary"
    11  	"errors"
    12  	"io"
    13  	"strings"
    14  
    15  	"github.com/Yawning/chacha20"
    16  	"golang.org/x/crypto/blowfish"
    17  	"golang.org/x/crypto/cast5"
    18  	"golang.org/x/crypto/salsa20/salsa"
    19  )
    20  
    21  var errEmptyPassword = errors.New("empty key")
    22  
    23  func md5sum(d []byte) []byte {
    24  	h := md5.New()
    25  	h.Write(d)
    26  	return h.Sum(nil)
    27  }
    28  
    29  func evpBytesToKey(password string, keyLen int) (key []byte) {
    30  	const md5Len = 16
    31  
    32  	cnt := (keyLen-1)/md5Len + 1
    33  	m := make([]byte, cnt*md5Len)
    34  	copy(m, md5sum([]byte(password)))
    35  
    36  	// Repeatedly call md5 until bytes generated is enough.
    37  	// Each call to md5 uses data: prev md5 sum + password.
    38  	d := make([]byte, md5Len+len(password))
    39  	start := 0
    40  	for i := 1; i < cnt; i++ {
    41  		start += md5Len
    42  		copy(d, m[start-md5Len:start])
    43  		copy(d[md5Len:], password)
    44  		copy(m[start:], md5sum(d))
    45  	}
    46  	return m[:keyLen]
    47  }
    48  
    49  type DecOrEnc int
    50  
    51  const (
    52  	Decrypt DecOrEnc = iota
    53  	Encrypt
    54  )
    55  
    56  func newStream(block cipher.Block, err error, key, iv []byte,
    57  	doe DecOrEnc) (cipher.Stream, error) {
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	if doe == Encrypt {
    62  		return cipher.NewCFBEncrypter(block, iv), nil
    63  	} else {
    64  		return cipher.NewCFBDecrypter(block, iv), nil
    65  	}
    66  }
    67  
    68  func newAESCFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
    69  	block, err := aes.NewCipher(key)
    70  	return newStream(block, err, key, iv, doe)
    71  }
    72  
    73  func newAESCTRStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
    74  	block, err := aes.NewCipher(key)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return cipher.NewCTR(block, iv), nil
    79  }
    80  
    81  func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
    82  	block, err := des.NewCipher(key)
    83  	return newStream(block, err, key, iv, doe)
    84  }
    85  
    86  func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
    87  	block, err := blowfish.NewCipher(key)
    88  	return newStream(block, err, key, iv, doe)
    89  }
    90  
    91  func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
    92  	block, err := cast5.NewCipher(key)
    93  	return newStream(block, err, key, iv, doe)
    94  }
    95  
    96  func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
    97  	h := md5.New()
    98  	h.Write(key)
    99  	h.Write(iv)
   100  	rc4key := h.Sum(nil)
   101  
   102  	return rc4.NewCipher(rc4key)
   103  }
   104  
   105  func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
   106  	return chacha20.NewCipher(key, iv)
   107  }
   108  
   109  func newChaCha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
   110  	return chacha20.NewCipher(key, iv)
   111  }
   112  
   113  type salsaStreamCipher struct {
   114  	nonce   [8]byte
   115  	key     [32]byte
   116  	counter int
   117  }
   118  
   119  func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) {
   120  	var buf []byte
   121  	padLen := c.counter % 64
   122  	dataSize := len(src) + padLen
   123  	if cap(dst) >= dataSize {
   124  		buf = dst[:dataSize]
   125  	} else if leakyBufSize >= dataSize {
   126  		buf = leakyBuf.Get()
   127  		defer leakyBuf.Put(buf)
   128  		buf = buf[:dataSize]
   129  	} else {
   130  		buf = make([]byte, dataSize)
   131  	}
   132  
   133  	var subNonce [16]byte
   134  	copy(subNonce[:], c.nonce[:])
   135  	binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64))
   136  
   137  	// It's difficult to avoid data copy here. src or dst maybe slice from
   138  	// Conn.Read/Write, which can't have padding.
   139  	copy(buf[padLen:], src[:])
   140  	salsa.XORKeyStream(buf, buf, &subNonce, &c.key)
   141  	copy(dst, buf[padLen:])
   142  
   143  	c.counter += len(src)
   144  }
   145  
   146  func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
   147  	var c salsaStreamCipher
   148  	copy(c.nonce[:], iv[:8])
   149  	copy(c.key[:], key[:32])
   150  	return &c, nil
   151  }
   152  
   153  type cipherInfo struct {
   154  	keyLen    int
   155  	ivLen     int
   156  	newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error)
   157  }
   158  
   159  var cipherMethod = map[string]*cipherInfo{
   160  	"aes-128-cfb":   {16, 16, newAESCFBStream},
   161  	"aes-192-cfb":   {24, 16, newAESCFBStream},
   162  	"aes-256-cfb":   {32, 16, newAESCFBStream},
   163  	"aes-128-ctr":   {16, 16, newAESCTRStream},
   164  	"aes-192-ctr":   {24, 16, newAESCTRStream},
   165  	"aes-256-ctr":   {32, 16, newAESCTRStream},
   166  	"des-cfb":       {8, 8, newDESStream},
   167  	"bf-cfb":        {16, 8, newBlowFishStream},
   168  	"cast5-cfb":     {16, 8, newCast5Stream},
   169  	"rc4-md5":       {16, 16, newRC4MD5Stream},
   170  	"rc4-md5-6":     {16, 6, newRC4MD5Stream},
   171  	"chacha20":      {32, 8, newChaCha20Stream},
   172  	"chacha20-ietf": {32, 12, newChaCha20IETFStream},
   173  	"salsa20":       {32, 8, newSalsa20Stream},
   174  }
   175  
   176  func CheckCipherMethod(method string) error {
   177  	if method == "" {
   178  		method = "aes-256-cfb"
   179  	}
   180  	_, ok := cipherMethod[method]
   181  	if !ok {
   182  		return errors.New("Unsupported encryption method: " + method)
   183  	}
   184  	return nil
   185  }
   186  
   187  type Cipher struct {
   188  	enc  cipher.Stream
   189  	dec  cipher.Stream
   190  	key  []byte
   191  	info *cipherInfo
   192  	ota  bool // one-time auth
   193  	iv   []byte
   194  }
   195  
   196  // NewCipher creates a cipher that can be used in Dial() etc.
   197  // Use cipher.Copy() to create a new cipher with the same method and password
   198  // to avoid the cost of repeated cipher initialization.
   199  func NewCipher(method, password string) (c *Cipher, err error) {
   200  	if password == "" {
   201  		return nil, errEmptyPassword
   202  	}
   203  	var ota bool
   204  	if strings.HasSuffix(strings.ToLower(method), "-auth") {
   205  		method = method[:len(method)-5] // len("-auth") = 5
   206  		ota = true
   207  	} else {
   208  		ota = false
   209  	}
   210  	mi, ok := cipherMethod[method]
   211  	if !ok {
   212  		return nil, errors.New("Unsupported encryption method: " + method)
   213  	}
   214  
   215  	key := evpBytesToKey(password, mi.keyLen)
   216  
   217  	c = &Cipher{key: key, info: mi}
   218  
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	c.ota = ota
   223  	return c, nil
   224  }
   225  
   226  // Initializes the block cipher with CFB mode, returns IV.
   227  func (c *Cipher) initEncrypt() (iv []byte, err error) {
   228  	if c.iv == nil {
   229  		iv = make([]byte, c.info.ivLen)
   230  		if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   231  			return nil, err
   232  		}
   233  		c.iv = iv
   234  	} else {
   235  		iv = c.iv
   236  	}
   237  	c.enc, err = c.info.newStream(c.key, iv, Encrypt)
   238  	return
   239  }
   240  
   241  func (c *Cipher) initDecrypt(iv []byte) (err error) {
   242  	c.dec, err = c.info.newStream(c.key, iv, Decrypt)
   243  	return
   244  }
   245  
   246  func (c *Cipher) encrypt(dst, src []byte) {
   247  	c.enc.XORKeyStream(dst, src)
   248  }
   249  
   250  func (c *Cipher) decrypt(dst, src []byte) {
   251  	c.dec.XORKeyStream(dst, src)
   252  }
   253  func (c *Cipher) Encrypt(src []byte) (cipherData []byte) {
   254  	cipher := c.Copy()
   255  	iv, err := cipher.initEncrypt()
   256  	if err != nil {
   257  		return
   258  	}
   259  	packetLen := len(src) + len(iv)
   260  	cipherData = make([]byte, packetLen)
   261  	copy(cipherData, iv)
   262  	cipher.encrypt(cipherData[len(iv):], src)
   263  	return
   264  }
   265  
   266  func (c *Cipher) Decrypt(src []byte) (data []byte) {
   267  	cipher := c.Copy()
   268  	if len(src) < c.info.ivLen {
   269  		return
   270  	}
   271  	iv := make([]byte, c.info.ivLen)
   272  	copy(iv, src[:c.info.ivLen])
   273  	if err := cipher.initDecrypt(iv); err != nil {
   274  		return
   275  	}
   276  	data = make([]byte, len(src)-len(iv))
   277  	cipher.decrypt(data[0:], src[c.info.ivLen:])
   278  	return
   279  }
   280  
   281  // Copy creates a new cipher at it's initial state.
   282  func (c *Cipher) Copy() *Cipher {
   283  	// This optimization maybe not necessary. But without this function, we
   284  	// need to maintain a table cache for newTableCipher and use lock to
   285  	// protect concurrent access to that cache.
   286  
   287  	// AES and DES ciphers does not return specific types, so it's difficult
   288  	// to create copy. But their initizliation time is less than 4000ns on my
   289  	// 2.26 GHz Intel Core 2 Duo processor. So no need to worry.
   290  
   291  	// Currently, blow-fish and cast5 initialization cost is an order of
   292  	// maganitude slower than other ciphers. (I'm not sure whether this is
   293  	// because the current implementation is not highly optimized, or this is
   294  	// the nature of the algorithm.)
   295  
   296  	nc := *c
   297  	nc.enc = nil
   298  	nc.dec = nil
   299  	nc.ota = c.ota
   300  	return &nc
   301  }