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