github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/shadowsocksr/cipher/cipher.go (about) 1 package cipher 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/des" 7 "crypto/md5" 8 "crypto/rc4" 9 "encoding/binary" 10 11 "github.com/Asutorufa/yuhaiin/pkg/net/proxy/shadowsocksr/cipher/camellia" 12 "github.com/Asutorufa/yuhaiin/pkg/net/proxy/shadowsocksr/cipher/idea" 13 "github.com/Asutorufa/yuhaiin/pkg/net/proxy/shadowsocksr/cipher/rc2" 14 "github.com/Asutorufa/yuhaiin/pkg/utils/pool" 15 "golang.org/x/crypto/blowfish" 16 "golang.org/x/crypto/cast5" 17 "golang.org/x/crypto/chacha20" 18 "golang.org/x/crypto/salsa20/salsa" 19 ) 20 21 func newAESCTRStream(key, iv []byte, _ bool) (cipher.Stream, error) { 22 block, err := aes.NewCipher(key) 23 if err != nil { 24 return nil, err 25 } 26 return cipher.NewCTR(block, iv), nil 27 } 28 29 func newAESOFBStream(key, iv []byte, _ bool) (cipher.Stream, error) { 30 block, err := aes.NewCipher(key) 31 if err != nil { 32 return nil, err 33 } 34 return cipher.NewOFB(block, iv), nil 35 } 36 37 func newRC4MD5Stream(key, iv []byte, _ bool) (cipher.Stream, error) { 38 rc4key := md5.Sum(append(key, iv...)) 39 return rc4.NewCipher(rc4key[:]) 40 } 41 42 func newChaCha20Stream(key, iv []byte, _ bool) (cipher.Stream, error) { 43 return chacha20.NewUnauthenticatedCipher(key, iv) 44 } 45 46 type salsaStreamCipher struct { 47 nonce [8]byte 48 key [32]byte 49 counter int 50 } 51 52 func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { 53 var buf []byte 54 padLen := c.counter % 64 55 dataSize := len(src) + padLen 56 if cap(dst) >= dataSize { 57 buf = dst[:dataSize] 58 } else if pool.DefaultSize >= dataSize { 59 buf = pool.GetBytes(pool.DefaultSize) 60 defer pool.PutBytes(buf) 61 buf = buf[:dataSize] 62 } else { 63 buf = make([]byte, dataSize) 64 } 65 66 var subNonce [16]byte 67 copy(subNonce[:], c.nonce[:]) 68 binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) 69 70 // It's difficult to avoid data copy here. src or dst maybe slice from 71 // Conn.Read/Write, which can't have padding. 72 copy(buf[padLen:], src[:]) 73 salsa.XORKeyStream(buf, buf, &subNonce, &c.key) 74 copy(dst, buf[padLen:]) 75 76 c.counter += len(src) 77 } 78 79 func newSalsa20Stream(key, iv []byte, _ bool) (cipher.Stream, error) { 80 var c salsaStreamCipher 81 copy(c.nonce[:], iv[:8]) 82 copy(c.key[:], key[:32]) 83 84 return &c, nil 85 } 86 87 func newRC4Stream(key, iv []byte, doe bool) (cipher.Stream, error) { 88 return rc4.NewCipher(key) 89 } 90 91 func newCFBStream(block cipher.Block, err error, iv []byte, decrypt bool) (cipher.Stream, error) { 92 if err != nil { 93 return nil, err 94 } 95 if !decrypt { 96 return cipher.NewCFBEncrypter(block, iv), nil 97 } else { 98 return cipher.NewCFBDecrypter(block, iv), nil 99 } 100 } 101 102 func newAESCFBStream(key, iv []byte, doe bool) (cipher.Stream, error) { 103 block, err := aes.NewCipher(key) 104 return newCFBStream(block, err, iv, doe) 105 } 106 107 func newDESStream(key, iv []byte, doe bool) (cipher.Stream, error) { 108 block, err := des.NewCipher(key) 109 return newCFBStream(block, err, iv, doe) 110 } 111 112 func newBlowFishStream(key, iv []byte, doe bool) (cipher.Stream, error) { 113 aes.NewCipher(key) 114 block, err := blowfish.NewCipher(key) 115 return newCFBStream(block, err, iv, doe) 116 } 117 118 func newCast5Stream(key, iv []byte, doe bool) (cipher.Stream, error) { 119 block, err := cast5.NewCipher(key) 120 return newCFBStream(block, err, iv, doe) 121 } 122 123 func newCamelliaStream(key, iv []byte, doe bool) (cipher.Stream, error) { 124 block, err := camellia.NewCipher(key) 125 return newCFBStream(block, err, iv, doe) 126 } 127 128 func newIdeaStream(key, iv []byte, doe bool) (cipher.Stream, error) { 129 block, err := idea.NewCipher(key) 130 return newCFBStream(block, err, iv, doe) 131 } 132 133 func newRC2Stream(key, iv []byte, doe bool) (cipher.Stream, error) { 134 block, err := rc2.New(key, 16) 135 return newCFBStream(block, err, iv, doe) 136 } 137 138 func newSeedStream(key, iv []byte, doe bool) (cipher.Stream, error) { 139 // TODO: SEED block cipher implementation is required 140 block, err := rc2.New(key, 16) 141 return newCFBStream(block, err, iv, doe) 142 } 143 144 type NoneStream struct{} 145 146 func (NoneStream) XORKeyStream(dst, src []byte) { copy(dst, src) } 147 func newNoneStream(key, iv []byte, doe bool) (cipher.Stream, error) { return new(NoneStream), nil } 148 149 type CipherFactory interface { 150 IVSize() int 151 EncryptStream(iv []byte) (cipher.Stream, error) 152 DecryptStream(iv []byte) (cipher.Stream, error) 153 } 154 type cipherFactory struct { 155 key []byte 156 ivSize int 157 stream func(key, iv []byte, decrypt bool) (cipher.Stream, error) 158 } 159 160 func (c cipherFactory) IVSize() int { return c.ivSize } 161 func (c cipherFactory) EncryptStream(iv []byte) (cipher.Stream, error) { 162 return c.stream(c.key, iv, false) 163 } 164 func (c cipherFactory) DecryptStream(iv []byte) (cipher.Stream, error) { 165 return c.stream(c.key, iv, true) 166 } 167 168 func newCipherObserver(keySize, ivSize int, stream func(key, iv []byte, decrypt bool) (cipher.Stream, error)) struct { 169 KeySize int 170 Creator func(key []byte) CipherFactory 171 } { 172 return struct { 173 KeySize int 174 Creator func(key []byte) CipherFactory 175 }{ 176 KeySize: keySize, 177 Creator: func(key []byte) CipherFactory { 178 return cipherFactory{key, ivSize, stream} 179 }, 180 } 181 } 182 183 var StreamCipherMethod = map[string]struct { 184 KeySize int 185 Creator func(key []byte) CipherFactory 186 }{ 187 "aes-128-cfb": newCipherObserver(16, 16, newAESCFBStream), 188 "aes-192-cfb": newCipherObserver(24, 16, newAESCFBStream), 189 "aes-256-cfb": newCipherObserver(32, 16, newAESCFBStream), 190 "aes-128-ctr": newCipherObserver(16, 16, newAESCTRStream), 191 "aes-192-ctr": newCipherObserver(24, 16, newAESCTRStream), 192 "aes-256-ctr": newCipherObserver(32, 16, newAESCTRStream), 193 "aes-128-ofb": newCipherObserver(16, 16, newAESOFBStream), 194 "aes-192-ofb": newCipherObserver(24, 16, newAESOFBStream), 195 "aes-256-ofb": newCipherObserver(32, 16, newAESOFBStream), 196 "des-cfb": newCipherObserver(8, 8, newDESStream), 197 "bf-cfb": newCipherObserver(16, 8, newBlowFishStream), 198 "cast5-cfb": newCipherObserver(16, 8, newCast5Stream), 199 "rc4-md5": newCipherObserver(16, 16, newRC4MD5Stream), 200 "rc4-md5-6": newCipherObserver(16, 6, newRC4MD5Stream), 201 "chacha20": newCipherObserver(chacha20.KeySize, 8, newChaCha20Stream), 202 "chacha20-ietf": newCipherObserver(chacha20.KeySize, chacha20.NonceSize, newChaCha20Stream), 203 "salsa20": newCipherObserver(32, 8, newSalsa20Stream), 204 "camellia-128-cfb": newCipherObserver(16, 16, newCamelliaStream), 205 "camellia-192-cfb": newCipherObserver(24, 16, newCamelliaStream), 206 "camellia-256-cfb": newCipherObserver(32, 16, newCamelliaStream), 207 "idea-cfb": newCipherObserver(16, 8, newIdeaStream), 208 "rc2-cfb": newCipherObserver(16, 8, newRC2Stream), 209 "seed-cfb": newCipherObserver(16, 8, newSeedStream), 210 "rc4": newCipherObserver(16, 0, newRC4Stream), 211 "none": newCipherObserver(16, 0, newNoneStream), 212 }