github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/core/lib/transport/encrypt/encrypt.go (about)

     1  package encrypt
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/des"
     7  	"crypto/md5"
     8  	"crypto/rc4"
     9  	"crypto/sha256"
    10  	"errors"
    11  
    12  	lbuf "github.com/AntonOrnatskyi/goproxy/core/lib/buf"
    13  	"github.com/Yawning/chacha20"
    14  	"golang.org/x/crypto/blowfish"
    15  	"golang.org/x/crypto/cast5"
    16  )
    17  
    18  const leakyBufSize = 2048
    19  const maxNBuf = 2048
    20  
    21  var leakyBuf = lbuf.NewLeakyBuf(maxNBuf, leakyBufSize)
    22  var errEmptyPassword = errors.New("proxy key")
    23  
    24  func md5sum(d []byte) []byte {
    25  	h := md5.New()
    26  	h.Write(d)
    27  	return h.Sum(nil)
    28  }
    29  
    30  func evpBytesToKey(password string, keyLen int) (key []byte) {
    31  	const md5Len = 16
    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 cipherInfo struct {
   114  	keyLen    int
   115  	ivLen     int
   116  	newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error)
   117  }
   118  
   119  var cipherMethod = map[string]*cipherInfo{
   120  	"aes-128-cfb":   {16, 16, newAESCFBStream},
   121  	"aes-192-cfb":   {24, 16, newAESCFBStream},
   122  	"aes-256-cfb":   {32, 16, newAESCFBStream},
   123  	"aes-128-ctr":   {16, 16, newAESCTRStream},
   124  	"aes-192-ctr":   {24, 16, newAESCTRStream},
   125  	"aes-256-ctr":   {32, 16, newAESCTRStream},
   126  	"des-cfb":       {8, 8, newDESStream},
   127  	"bf-cfb":        {16, 8, newBlowFishStream},
   128  	"cast5-cfb":     {16, 8, newCast5Stream},
   129  	"rc4-md5":       {16, 16, newRC4MD5Stream},
   130  	"rc4-md5-6":     {16, 6, newRC4MD5Stream},
   131  	"chacha20":      {32, 8, newChaCha20Stream},
   132  	"chacha20-ietf": {32, 12, newChaCha20IETFStream},
   133  }
   134  
   135  func GetCipherMethods() (keys []string) {
   136  	keys = []string{}
   137  	for k := range cipherMethod {
   138  		keys = append(keys, k)
   139  	}
   140  	return
   141  }
   142  func CheckCipherMethod(method string) error {
   143  	if method == "" {
   144  		method = "aes-256-cfb"
   145  	}
   146  	_, ok := cipherMethod[method]
   147  	if !ok {
   148  		return errors.New("Unsupported encryption method: " + method)
   149  	}
   150  	return nil
   151  }
   152  
   153  type Cipher struct {
   154  	WriteStream cipher.Stream
   155  	ReadStream  cipher.Stream
   156  	key         []byte
   157  	info        *cipherInfo
   158  }
   159  
   160  func NewCipher(method, password string) (c *Cipher, err error) {
   161  	if password == "" {
   162  		return nil, errEmptyPassword
   163  	}
   164  	mi, ok := cipherMethod[method]
   165  	if !ok {
   166  		return nil, errors.New("Unsupported encryption method: " + method)
   167  	}
   168  	key := evpBytesToKey(password, mi.keyLen)
   169  	c = &Cipher{key: key, info: mi}
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	//hash(key) -> read IV
   174  	riv := sha256.New().Sum(c.key)[:c.info.ivLen]
   175  	c.ReadStream, err = c.info.newStream(c.key, riv, Decrypt)
   176  	if err != nil {
   177  		return nil, err
   178  	} //hash(read IV) -> write IV
   179  	wiv := sha256.New().Sum(riv)[:c.info.ivLen]
   180  	c.WriteStream, err = c.info.newStream(c.key, wiv, Encrypt)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	return c, nil
   185  }