Chore: embed shadowsocks2

This commit is contained in:
Dreamacro
2022-05-24 20:28:18 +08:00
parent 3360839fe3
commit 09d49bac95
19 changed files with 1045 additions and 21 deletions

View File

@ -0,0 +1,94 @@
package shadowaead
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha1"
"io"
"strconv"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
)
type Cipher interface {
KeySize() int
SaltSize() int
Encrypter(salt []byte) (cipher.AEAD, error)
Decrypter(salt []byte) (cipher.AEAD, error)
}
type KeySizeError int
func (e KeySizeError) Error() string {
return "key size error: need " + strconv.Itoa(int(e)) + " bytes"
}
func hkdfSHA1(secret, salt, info, outkey []byte) {
r := hkdf.New(sha1.New, secret, salt, info)
if _, err := io.ReadFull(r, outkey); err != nil {
panic(err) // should never happen
}
}
type metaCipher struct {
psk []byte
makeAEAD func(key []byte) (cipher.AEAD, error)
}
func (a *metaCipher) KeySize() int { return len(a.psk) }
func (a *metaCipher) SaltSize() int {
if ks := a.KeySize(); ks > 16 {
return ks
}
return 16
}
func (a *metaCipher) Encrypter(salt []byte) (cipher.AEAD, error) {
subkey := make([]byte, a.KeySize())
hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey)
return a.makeAEAD(subkey)
}
func (a *metaCipher) Decrypter(salt []byte) (cipher.AEAD, error) {
subkey := make([]byte, a.KeySize())
hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey)
return a.makeAEAD(subkey)
}
func aesGCM(key []byte) (cipher.AEAD, error) {
blk, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewGCM(blk)
}
// AESGCM creates a new Cipher with a pre-shared key. len(psk) must be
// one of 16, 24, or 32 to select AES-128/196/256-GCM.
func AESGCM(psk []byte) (Cipher, error) {
switch l := len(psk); l {
case 16, 24, 32: // AES 128/196/256
default:
return nil, aes.KeySizeError(l)
}
return &metaCipher{psk: psk, makeAEAD: aesGCM}, nil
}
// Chacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk)
// must be 32.
func Chacha20Poly1305(psk []byte) (Cipher, error) {
if len(psk) != chacha20poly1305.KeySize {
return nil, KeySizeError(chacha20poly1305.KeySize)
}
return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.New}, nil
}
// XChacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk)
// must be 32.
func XChacha20Poly1305(psk []byte) (Cipher, error) {
if len(psk) != chacha20poly1305.KeySize {
return nil, KeySizeError(chacha20poly1305.KeySize)
}
return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.NewX}, nil
}

View File

@ -0,0 +1,95 @@
package shadowaead
import (
"crypto/rand"
"errors"
"io"
"net"
"github.com/Dreamacro/clash/common/pool"
)
// ErrShortPacket means that the packet is too short for a valid encrypted packet.
var ErrShortPacket = errors.New("short packet")
var _zerononce [128]byte // read-only. 128 bytes is more than enough.
// Pack encrypts plaintext using Cipher with a randomly generated salt and
// returns a slice of dst containing the encrypted packet and any error occurred.
// Ensure len(dst) >= ciph.SaltSize() + len(plaintext) + aead.Overhead().
func Pack(dst, plaintext []byte, ciph Cipher) ([]byte, error) {
saltSize := ciph.SaltSize()
salt := dst[:saltSize]
if _, err := rand.Read(salt); err != nil {
return nil, err
}
aead, err := ciph.Encrypter(salt)
if err != nil {
return nil, err
}
if len(dst) < saltSize+len(plaintext)+aead.Overhead() {
return nil, io.ErrShortBuffer
}
b := aead.Seal(dst[saltSize:saltSize], _zerononce[:aead.NonceSize()], plaintext, nil)
return dst[:saltSize+len(b)], nil
}
// Unpack decrypts pkt using Cipher and returns a slice of dst containing the decrypted payload and any error occurred.
// Ensure len(dst) >= len(pkt) - aead.SaltSize() - aead.Overhead().
func Unpack(dst, pkt []byte, ciph Cipher) ([]byte, error) {
saltSize := ciph.SaltSize()
if len(pkt) < saltSize {
return nil, ErrShortPacket
}
salt := pkt[:saltSize]
aead, err := ciph.Decrypter(salt)
if err != nil {
return nil, err
}
if len(pkt) < saltSize+aead.Overhead() {
return nil, ErrShortPacket
}
if saltSize+len(dst)+aead.Overhead() < len(pkt) {
return nil, io.ErrShortBuffer
}
b, err := aead.Open(dst[:0], _zerononce[:aead.NonceSize()], pkt[saltSize:], nil)
return b, err
}
type PacketConn struct {
net.PacketConn
Cipher
}
const maxPacketSize = 64 * 1024
// NewPacketConn wraps a net.PacketConn with cipher
func NewPacketConn(c net.PacketConn, ciph Cipher) *PacketConn {
return &PacketConn{PacketConn: c, Cipher: ciph}
}
// WriteTo encrypts b and write to addr using the embedded PacketConn.
func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
buf := pool.Get(maxPacketSize)
defer pool.Put(buf)
buf, err := Pack(buf, b, c)
if err != nil {
return 0, err
}
_, err = c.PacketConn.WriteTo(buf, addr)
return len(b), err
}
// ReadFrom reads from the embedded PacketConn and decrypts into b.
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, addr, err := c.PacketConn.ReadFrom(b)
if err != nil {
return n, addr, err
}
bb, err := Unpack(b[c.Cipher.SaltSize():], b[:n], c)
if err != nil {
return n, addr, err
}
copy(b, bb)
return len(bb), addr, err
}

View File

@ -0,0 +1,285 @@
package shadowaead
import (
"crypto/cipher"
"crypto/rand"
"errors"
"io"
"net"
"github.com/Dreamacro/clash/common/pool"
)
const (
// payloadSizeMask is the maximum size of payload in bytes.
payloadSizeMask = 0x3FFF // 16*1024 - 1
bufSize = 17 * 1024 // >= 2+aead.Overhead()+payloadSizeMask+aead.Overhead()
)
var ErrZeroChunk = errors.New("zero chunk")
type Writer struct {
io.Writer
cipher.AEAD
nonce [32]byte // should be sufficient for most nonce sizes
}
// NewWriter wraps an io.Writer with authenticated encryption.
func NewWriter(w io.Writer, aead cipher.AEAD) *Writer { return &Writer{Writer: w, AEAD: aead} }
// Write encrypts p and writes to the embedded io.Writer.
func (w *Writer) Write(p []byte) (n int, err error) {
buf := pool.Get(bufSize)
defer pool.Put(buf)
nonce := w.nonce[:w.NonceSize()]
tag := w.Overhead()
off := 2 + tag
// compatible with snell
if len(p) == 0 {
buf = buf[:off]
buf[0], buf[1] = byte(0), byte(0)
w.Seal(buf[:0], nonce, buf[:2], nil)
increment(nonce)
_, err = w.Writer.Write(buf)
return
}
for nr := 0; n < len(p) && err == nil; n += nr {
nr = payloadSizeMask
if n+nr > len(p) {
nr = len(p) - n
}
buf = buf[:off+nr+tag]
buf[0], buf[1] = byte(nr>>8), byte(nr) // big-endian payload size
w.Seal(buf[:0], nonce, buf[:2], nil)
increment(nonce)
w.Seal(buf[:off], nonce, p[n:n+nr], nil)
increment(nonce)
_, err = w.Writer.Write(buf)
}
return
}
// ReadFrom reads from the given io.Reader until EOF or error, encrypts and
// writes to the embedded io.Writer. Returns number of bytes read from r and
// any error encountered.
func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
buf := pool.Get(bufSize)
defer pool.Put(buf)
nonce := w.nonce[:w.NonceSize()]
tag := w.Overhead()
off := 2 + tag
for {
nr, er := r.Read(buf[off : off+payloadSizeMask])
n += int64(nr)
buf[0], buf[1] = byte(nr>>8), byte(nr)
w.Seal(buf[:0], nonce, buf[:2], nil)
increment(nonce)
w.Seal(buf[:off], nonce, buf[off:off+nr], nil)
increment(nonce)
if _, ew := w.Writer.Write(buf[:off+nr+tag]); ew != nil {
err = ew
return
}
if er != nil {
if er != io.EOF { // ignore EOF as per io.ReaderFrom contract
err = er
}
return
}
}
}
type Reader struct {
io.Reader
cipher.AEAD
nonce [32]byte // should be sufficient for most nonce sizes
buf []byte // to be put back into bufPool
off int // offset to unconsumed part of buf
}
// NewReader wraps an io.Reader with authenticated decryption.
func NewReader(r io.Reader, aead cipher.AEAD) *Reader { return &Reader{Reader: r, AEAD: aead} }
// Read and decrypt a record into p. len(p) >= max payload size + AEAD overhead.
func (r *Reader) read(p []byte) (int, error) {
nonce := r.nonce[:r.NonceSize()]
tag := r.Overhead()
// decrypt payload size
p = p[:2+tag]
if _, err := io.ReadFull(r.Reader, p); err != nil {
return 0, err
}
_, err := r.Open(p[:0], nonce, p, nil)
increment(nonce)
if err != nil {
return 0, err
}
// decrypt payload
size := (int(p[0])<<8 + int(p[1])) & payloadSizeMask
if size == 0 {
return 0, ErrZeroChunk
}
p = p[:size+tag]
if _, err := io.ReadFull(r.Reader, p); err != nil {
return 0, err
}
_, err = r.Open(p[:0], nonce, p, nil)
increment(nonce)
if err != nil {
return 0, err
}
return size, nil
}
// Read reads from the embedded io.Reader, decrypts and writes to p.
func (r *Reader) Read(p []byte) (int, error) {
if r.buf == nil {
if len(p) >= payloadSizeMask+r.Overhead() {
return r.read(p)
}
b := pool.Get(bufSize)
n, err := r.read(b)
if err != nil {
return 0, err
}
r.buf = b[:n]
r.off = 0
}
n := copy(p, r.buf[r.off:])
r.off += n
if r.off == len(r.buf) {
pool.Put(r.buf[:cap(r.buf)])
r.buf = nil
}
return n, nil
}
// WriteTo reads from the embedded io.Reader, decrypts and writes to w until
// there's no more data to write or when an error occurs. Return number of
// bytes written to w and any error encountered.
func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
if r.buf == nil {
r.buf = pool.Get(bufSize)
r.off = len(r.buf)
}
for {
for r.off < len(r.buf) {
nw, ew := w.Write(r.buf[r.off:])
r.off += nw
n += int64(nw)
if ew != nil {
if r.off == len(r.buf) {
pool.Put(r.buf[:cap(r.buf)])
r.buf = nil
}
err = ew
return
}
}
nr, er := r.read(r.buf)
if er != nil {
if er != io.EOF {
err = er
}
return
}
r.buf = r.buf[:nr]
r.off = 0
}
}
// increment little-endian encoded unsigned integer b. Wrap around on overflow.
func increment(b []byte) {
for i := range b {
b[i]++
if b[i] != 0 {
return
}
}
}
type Conn struct {
net.Conn
Cipher
r *Reader
w *Writer
}
// NewConn wraps a stream-oriented net.Conn with cipher.
func NewConn(c net.Conn, ciph Cipher) *Conn { return &Conn{Conn: c, Cipher: ciph} }
func (c *Conn) initReader() error {
salt := make([]byte, c.SaltSize())
if _, err := io.ReadFull(c.Conn, salt); err != nil {
return err
}
aead, err := c.Decrypter(salt)
if err != nil {
return err
}
c.r = NewReader(c.Conn, aead)
return nil
}
func (c *Conn) Read(b []byte) (int, error) {
if c.r == nil {
if err := c.initReader(); err != nil {
return 0, err
}
}
return c.r.Read(b)
}
func (c *Conn) WriteTo(w io.Writer) (int64, error) {
if c.r == nil {
if err := c.initReader(); err != nil {
return 0, err
}
}
return c.r.WriteTo(w)
}
func (c *Conn) initWriter() error {
salt := make([]byte, c.SaltSize())
if _, err := rand.Read(salt); err != nil {
return err
}
aead, err := c.Encrypter(salt)
if err != nil {
return err
}
_, err = c.Conn.Write(salt)
if err != nil {
return err
}
c.w = NewWriter(c.Conn, aead)
return nil
}
func (c *Conn) Write(b []byte) (int, error) {
if c.w == nil {
if err := c.initWriter(); err != nil {
return 0, err
}
}
return c.w.Write(b)
}
func (c *Conn) ReadFrom(r io.Reader) (int64, error) {
if c.w == nil {
if err := c.initWriter(); err != nil {
return 0, err
}
}
return c.w.ReadFrom(r)
}