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