forked from riobard/go-shadowsocks2
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 443dd01
Showing
16 changed files
with
1,535 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/aes" | ||
"crypto/cipher" | ||
"errors" | ||
"fmt" | ||
"net" | ||
"strings" | ||
|
||
"golang.org/x/crypto/chacha20poly1305" | ||
|
||
"github.com/Yawning/chacha20" | ||
"github.com/riobard/go-shadowsocks2/core" | ||
"github.com/riobard/go-shadowsocks2/shadowaead" | ||
"github.com/riobard/go-shadowsocks2/shadowstream" | ||
) | ||
|
||
// ErrKeySize means the supplied key size does not meet the requirement of cipher choosed. | ||
var ErrKeySize = errors.New("key size error") | ||
|
||
func pickCipher(name string, key []byte) (core.StreamConnCipher, core.PacketConnCipher, error) { | ||
|
||
switch strings.ToLower(name) { | ||
case "aes-128-gcm", "aes-192-gcm", "aes-256-gcm": | ||
aead, err := aesGCM(key, 0) // 0 for standard 12-byte nonce | ||
return aeadStream(aead), aeadPacket(aead), err | ||
|
||
case "aes-128-gcm-16", "aes-192-gcm-16", "aes-256-gcm-16": | ||
aead, err := aesGCM(key, 16) // 16-byte nonce for better collision avoidance | ||
return aeadStream(aead), aeadPacket(aead), err | ||
|
||
case "chacha20-ietf-poly1305": | ||
aead, err := chacha20poly1305.New(key) | ||
return aeadStream(aead), aeadPacket(aead), err | ||
|
||
case "aes-128-ctr", "aes-192-ctr", "aes-256-ctr": | ||
ciph, err := aesCTR(key) | ||
return streamStream(ciph), streamPacket(ciph), err | ||
|
||
case "aes-128-cfb", "aes-192-cfb", "aes-256-cfb": | ||
ciph, err := aesCFB(key) | ||
return streamStream(ciph), streamPacket(ciph), err | ||
|
||
case "chacha20-ietf": | ||
if len(key) != chacha20.KeySize { | ||
return nil, nil, ErrKeySize | ||
} | ||
k := chacha20ietfkey(key) | ||
return streamStream(k), streamPacket(k), nil | ||
|
||
case "dummy": // only for benchmarking and debugging | ||
return dummyStream(), dummyPacket(), nil | ||
|
||
default: | ||
err := fmt.Errorf("cipher not supported: %s", name) | ||
return nil, nil, err | ||
} | ||
} | ||
|
||
func dummyStream() core.StreamConnCipher { | ||
return func(c net.Conn) net.Conn { return c } | ||
} | ||
func dummyPacket() core.PacketConnCipher { | ||
return func(c net.PacketConn) net.PacketConn { return c } | ||
} | ||
|
||
func aeadStream(aead cipher.AEAD) core.StreamConnCipher { | ||
return func(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) } | ||
} | ||
func aeadPacket(aead cipher.AEAD) core.PacketConnCipher { | ||
return func(c net.PacketConn) net.PacketConn { return shadowaead.NewPacketConn(c, aead) } | ||
} | ||
|
||
func aesGCM(key []byte, nonceSize int) (cipher.AEAD, error) { | ||
blk, err := aes.NewCipher(key) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if nonceSize > 0 { | ||
return cipher.NewGCMWithNonceSize(blk, nonceSize) | ||
} | ||
return cipher.NewGCM(blk) // standard 12-byte nonce | ||
} | ||
|
||
func streamStream(ciph shadowstream.Cipher) core.StreamConnCipher { | ||
return func(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) } | ||
} | ||
|
||
func streamPacket(ciph shadowstream.Cipher) core.PacketConnCipher { | ||
return func(c net.PacketConn) net.PacketConn { return shadowstream.NewPacketConn(c, ciph) } | ||
} | ||
|
||
type ctrStream struct{ cipher.Block } | ||
|
||
func (b *ctrStream) IVSize() int { return b.BlockSize() } | ||
func (b *ctrStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCTR(b, iv) } | ||
func (b *ctrStream) Decrypter(iv []byte) cipher.Stream { return b.Encrypter(iv) } | ||
|
||
func aesCTR(key []byte) (shadowstream.Cipher, error) { | ||
blk, err := aes.NewCipher(key) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &ctrStream{blk}, nil | ||
} | ||
|
||
type cfbStream struct{ cipher.Block } | ||
|
||
func (b *cfbStream) IVSize() int { return b.BlockSize() } | ||
func (b *cfbStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCFBEncrypter(b, iv) } | ||
func (b *cfbStream) Decrypter(iv []byte) cipher.Stream { return cipher.NewCFBDecrypter(b, iv) } | ||
|
||
func aesCFB(key []byte) (shadowstream.Cipher, error) { | ||
blk, err := aes.NewCipher(key) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &ctrStream{blk}, nil | ||
} | ||
|
||
type chacha20ietfkey []byte | ||
|
||
func (k chacha20ietfkey) IVSize() int { return chacha20.INonceSize } | ||
func (k chacha20ietfkey) Encrypter(iv []byte) cipher.Stream { | ||
ciph, err := chacha20.NewCipher(k, iv) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return ciph | ||
} | ||
func (k chacha20ietfkey) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package core provides essential interfaces for Shadowsocks | ||
package core |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package core | ||
|
||
import "net" | ||
|
||
type PacketConnCipher func(net.PacketConn) net.PacketConn | ||
|
||
func ListenPacket(network, address string, ciph PacketConnCipher) (net.PacketConn, error) { | ||
c, err := net.ListenPacket(network, address) | ||
return ciph(c), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package core | ||
|
||
import "net" | ||
|
||
type StreamConnCipher func(net.Conn) net.Conn | ||
|
||
type listener struct { | ||
net.Listener | ||
StreamConnCipher | ||
} | ||
|
||
func Listen(network, address string, ciph StreamConnCipher) (net.Listener, error) { | ||
l, err := net.Listen(network, address) | ||
return &listener{l, ciph}, err | ||
} | ||
|
||
func (l *listener) Accept() (net.Conn, error) { | ||
c, err := l.Listener.Accept() | ||
return l.StreamConnCipher(c), err | ||
} | ||
|
||
func Dial(network, address string, ciph StreamConnCipher) (net.Conn, error) { | ||
c, err := net.Dial(network, address) | ||
return ciph(c), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/hex" | ||
"flag" | ||
"log" | ||
"os" | ||
"os/signal" | ||
"strings" | ||
"syscall" | ||
"time" | ||
) | ||
|
||
var config struct { | ||
Verbose bool | ||
UDPTimeout time.Duration | ||
} | ||
|
||
func logf(f string, v ...interface{}) { | ||
if config.Verbose { | ||
log.Printf(f, v...) | ||
} | ||
} | ||
|
||
func main() { | ||
|
||
var flags struct { | ||
Client string | ||
Server string | ||
Cipher string | ||
Key string | ||
Socks string | ||
RedirTCP string | ||
RedirTCP6 string | ||
TCPTun string | ||
UDPTun string | ||
} | ||
|
||
flag.BoolVar(&config.Verbose, "verbose", false, "verbose mode") | ||
flag.StringVar(&flags.Cipher, "cipher", "aes-128-gcm", "cipher to encrypt/decrypt") | ||
flag.StringVar(&flags.Key, "key", "", "secret key in hexadecimal") | ||
flag.StringVar(&flags.Server, "s", "", "server listen address") | ||
flag.StringVar(&flags.Client, "c", "", "client connect address") | ||
flag.StringVar(&flags.Socks, "socks", ":1080", "(client-only) SOCKS listen address") | ||
flag.StringVar(&flags.RedirTCP, "redir", "", "(client-only) redirect TCP from this address") | ||
flag.StringVar(&flags.RedirTCP6, "redir6", "", "(client-only) redirect TCP IPv6 from this address") | ||
flag.StringVar(&flags.TCPTun, "tcptun", "", "(client-only) TCP tunnel (laddr1=raddr1,laddr2=raddr2,...)") | ||
flag.StringVar(&flags.UDPTun, "udptun", "", "(client-only) UDP tunnel (laddr1=raddr1,laddr2=raddr2,...)") | ||
flag.DurationVar(&config.UDPTimeout, "udptimeout", 5*time.Minute, "UDP tunnel timeout") | ||
flag.Parse() | ||
|
||
key, err := hex.DecodeString(flags.Key) | ||
if err != nil { | ||
log.Fatalf("failed to parse key: %v", err) | ||
} | ||
|
||
streamCipher, packetCipher, err := pickCipher(flags.Cipher, key) | ||
if err != nil { | ||
log.Fatalf("failed to create cipher %s: %v", flags.Cipher, err) | ||
} | ||
|
||
if flags.Client != "" { // client mode | ||
if flags.UDPTun != "" { | ||
for _, tun := range strings.Split(flags.UDPTun, ",") { | ||
p := strings.Split(tun, "=") | ||
go udpLocal(p[0], flags.Client, p[1], packetCipher) | ||
} | ||
} | ||
|
||
if flags.TCPTun != "" { | ||
for _, tun := range strings.Split(flags.TCPTun, ",") { | ||
p := strings.Split(tun, "=") | ||
go tcpTun(p[0], flags.Client, p[1], streamCipher) | ||
} | ||
} | ||
|
||
if flags.Socks != "" { | ||
go socksLocal(flags.Socks, flags.Client, streamCipher) | ||
} | ||
|
||
if flags.RedirTCP != "" { | ||
go redirLocal(flags.RedirTCP, flags.Client, streamCipher) | ||
} | ||
|
||
if flags.RedirTCP6 != "" { | ||
go redir6Local(flags.RedirTCP6, flags.Client, streamCipher) | ||
} | ||
} else if flags.Server != "" { // server mode | ||
go udpRemote(flags.Server, packetCipher) | ||
go tcpRemote(flags.Server, streamCipher) | ||
} else { | ||
flag.Usage() | ||
return | ||
} | ||
|
||
sigCh := make(chan os.Signal, 1) | ||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) | ||
<-sigCh | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
Package shadowaead implements a simple AEAD-protected secure protocol. | ||
In general, there are two types of connections: stream-oriented and packet-oriented. | ||
Stream-oriented connections (e.g. TCP) assume reliable and orderly delivery of bytes. | ||
Packet-oriented connections (e.g. UDP) assume unreliable and out-of-order delivery of packets, | ||
where each packet is either delivered intact or lost. | ||
An encrypted stream starts with a nonce, followed by any number of encrypted records. | ||
Each encrypted record has the following structure: | ||
[encrypted payload length] | ||
[payload length tag] | ||
[encrypted payload] | ||
[payload tag] | ||
Payload length is 2-byte unsigned big-endian integer capped at 0x3FFF (16383). | ||
The higher 2 bits are reserved and must be set to zero. The first AEAD encrypt/decrypt | ||
operation uses the nonce at the beginning of the stream. After each encrypt/decrypt operation, | ||
the nonce is incremented by one as if it were an unsigned little-endian integer. | ||
Each encrypted packet transmitted on a packet-oriented connection has the following structure: | ||
[nonce] | ||
[encrypted payload] | ||
[payload tag] | ||
Packets are encrypted/decrypted independently. | ||
In both stream-oriented and packet-oriented connections, length of nonce and tag varies | ||
depending on which AEAD is used. Nonces are assumed to be randomly generated and | ||
of sufficient length (at least 12 bytes). | ||
*/ | ||
package shadowaead |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package shadowaead | ||
|
||
import ( | ||
"crypto/cipher" | ||
"crypto/rand" | ||
"errors" | ||
"io" | ||
"net" | ||
) | ||
|
||
// ErrShortPacket means that the packet is too short for a valid encrypted packet. | ||
var ErrShortPacket = errors.New("shadow: short packet") | ||
|
||
// Pack encrypts plaintext using aead with a randomly generated nonce and | ||
// returns a slice of dst containing the encrypted packet and any error occurred. | ||
// Ensure len(dst) >= aead.NonceSize() + len(plaintext) + aead.Overhead(). | ||
func Pack(dst, plaintext []byte, aead cipher.AEAD) ([]byte, error) { | ||
nsiz := aead.NonceSize() | ||
if len(dst) < nsiz+len(plaintext)+aead.Overhead() { | ||
return nil, io.ErrShortBuffer | ||
} | ||
|
||
nonce := dst[:nsiz] | ||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil { | ||
return nil, err | ||
} | ||
|
||
b := aead.Seal(dst[nsiz:nsiz], nonce, plaintext, nil) | ||
return dst[:nsiz+len(b)], nil | ||
} | ||
|
||
// Unpack decrypts pkt using aead and returns a slice of dst containing the decrypted payload and any error occurred. | ||
// Ensure len(dst) >= len(pkt) - aead.NonceSize() - aead.Overhead(). | ||
func Unpack(dst, pkt []byte, aead cipher.AEAD) ([]byte, error) { | ||
nsiz := aead.NonceSize() | ||
|
||
if len(pkt) < nsiz+aead.Overhead() { | ||
return nil, ErrShortPacket | ||
} | ||
|
||
if len(dst) < len(pkt)-nsiz-aead.Overhead() { | ||
return nil, io.ErrShortBuffer | ||
} | ||
|
||
b, err := aead.Open(dst[:0], pkt[:nsiz], pkt[nsiz:], nil) | ||
return b, err | ||
} | ||
|
||
// packetConn encrypts net.packetConn with cipher.AEAD | ||
type packetConn struct { | ||
net.PacketConn | ||
cipher.AEAD | ||
} | ||
|
||
// NewPacketConn wraps a net.PacketConn with AEAD protection. | ||
func NewPacketConn(c net.PacketConn, aead cipher.AEAD) net.PacketConn { | ||
return &packetConn{PacketConn: c, AEAD: aead} | ||
} | ||
|
||
// WriteTo encrypts b and write to addr using the embedded PacketConn. | ||
func (c *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { | ||
buf := make([]byte, c.AEAD.NonceSize()+len(b)+c.AEAD.Overhead()) | ||
buf, err := Pack(buf, b, c.AEAD) | ||
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 | ||
} | ||
b, err = Unpack(b, b[:n], c.AEAD) | ||
return len(b), addr, err | ||
} |
Oops, something went wrong.