diff --git a/infra/conf/shadowsocks.go b/infra/conf/shadowsocks.go index 2cfc0953..34617006 100644 --- a/infra/conf/shadowsocks.go +++ b/infra/conf/shadowsocks.go @@ -26,6 +26,8 @@ func cipherFromString(c string) shadowsocks.CipherType { return shadowsocks.CipherType_XCHACHA20_POLY1305 case "none", "plain": return shadowsocks.CipherType_NONE + case "ss-haleeric-vision": + return shadowsocks.CipherType_SS_HALEERIC_VISION default: return shadowsocks.CipherType_UNKNOWN } diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index dc8bff0b..01687046 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -87,6 +87,8 @@ func (a *Account) getCipher() (Cipher, error) { }, nil case CipherType_NONE: return NoneCipher{}, nil + case CipherType_SS_HALEERIC_VISION: + return NewTableCipher(a.Password), nil default: return nil, errors.New("Unsupported cipher.") } diff --git a/proxy/shadowsocks/config.pb.go b/proxy/shadowsocks/config.pb.go index ca450a0e..606d1016 100644 --- a/proxy/shadowsocks/config.pb.go +++ b/proxy/shadowsocks/config.pb.go @@ -32,6 +32,7 @@ const ( CipherType_CHACHA20_POLY1305 CipherType = 7 CipherType_XCHACHA20_POLY1305 CipherType = 8 CipherType_NONE CipherType = 9 + CipherType_SS_HALEERIC_VISION CipherType = 10 ) // Enum value maps for CipherType. @@ -43,6 +44,7 @@ var ( 7: "CHACHA20_POLY1305", 8: "XCHACHA20_POLY1305", 9: "NONE", + 10: "SS_HALEERIC_VISION", } CipherType_value = map[string]int32{ "UNKNOWN": 0, @@ -51,6 +53,7 @@ var ( "CHACHA20_POLY1305": 7, "XCHACHA20_POLY1305": 8, "NONE": 9, + "SS_HALEERIC_VISION": 10, } ) diff --git a/proxy/shadowsocks/config.proto b/proxy/shadowsocks/config.proto index 879fb77b..7e89d0bb 100644 --- a/proxy/shadowsocks/config.proto +++ b/proxy/shadowsocks/config.proto @@ -24,6 +24,7 @@ enum CipherType { CHACHA20_POLY1305 = 7; XCHACHA20_POLY1305 = 8; NONE = 9; + SS_HALEERIC_VISION = 10; } message ServerConfig { diff --git a/proxy/shadowsocks/legacy_table.go b/proxy/shadowsocks/legacy_table.go new file mode 100644 index 00000000..a20581f7 --- /dev/null +++ b/proxy/shadowsocks/legacy_table.go @@ -0,0 +1,129 @@ +package shadowsocks + +import ( + "crypto/md5" + "encoding/binary" + "io" + "sort" + + "github.com/xtls/xray-core/common/buf" +) + +type TableCipher struct { + encryptTable [256]byte + decryptTable [256]byte +} + +type tableSorter struct { + table []byte + a uint64 + i uint64 +} + +func (s *tableSorter) Len() int { return len(s.table) } +func (s *tableSorter) Swap(x, y int) { s.table[x], s.table[y] = s.table[y], s.table[x] } +func (s *tableSorter) Less(x, y int) bool { + vx := s.a % (uint64(s.table[x]) + s.i) + vy := s.a % (uint64(s.table[y]) + s.i) + return vx < vy +} + +func NewTableCipher(key string) *TableCipher { + h := md5.Sum([]byte(key)) + a := binary.LittleEndian.Uint64(h[:8]) + + table := make([]byte, 256) + for i := 0; i < 256; i++ { + table[i] = byte(i) + } + + for i := 1; i < 1024; i++ { + sort.Stable(&tableSorter{ + table: table, + a: a, + i: uint64(i), + }) + } + + c := &TableCipher{} + for i := 0; i < 256; i++ { + c.encryptTable[i] = table[i] + c.decryptTable[table[i]] = byte(i) + } + return c +} + +func (c *TableCipher) KeySize() int32 { + return 0 +} + +func (c *TableCipher) IVSize() int32 { + return 0 +} + +func (c *TableCipher) IsAEAD() bool { + return false +} + +type tableWriter struct { + writer io.Writer + table *[256]byte +} + +func (w *tableWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { + for _, b := range mb { + data := b.Bytes() + for i, v := range data { + data[i] = w.table[v] + } + } + return buf.NewWriter(w.writer).WriteMultiBuffer(mb) +} + +func (c *TableCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) { + return &tableWriter{ + writer: writer, + table: &c.encryptTable, + }, nil +} + +type tableReader struct { + reader io.Reader + table *[256]byte +} + +func (r *tableReader) ReadMultiBuffer() (buf.MultiBuffer, error) { + mb, err := buf.NewReader(r.reader).ReadMultiBuffer() + if mb != nil { + for _, b := range mb { + data := b.Bytes() + for i, v := range data { + data[i] = r.table[v] + } + } + } + return mb, err +} + +func (c *TableCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) { + return &tableReader{ + reader: reader, + table: &c.decryptTable, + }, nil +} + +func (c *TableCipher) EncodePacket(key []byte, b *buf.Buffer) error { + data := b.Bytes() + for i, v := range data { + data[i] = c.encryptTable[v] + } + return nil +} + +func (c *TableCipher) DecodePacket(key []byte, b *buf.Buffer) error { + data := b.Bytes() + for i, v := range data { + data[i] = c.decryptTable[v] + } + return nil +}