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 }