Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
c9be2f2
vless: improve vless xudp
cubatic45 Sep 28, 2025
bb1ed93
port shadowsocks 2022 from LostAttractor/next
AkinoKaede Feb 4, 2026
64452cf
ss2022: complete P0/P1 hardening and add protocol tests
Feb 16, 2026
967c12a
perf: optimize memory allocation in shadowsocks protocols
Feb 17, 2026
159974f
feat: implement performance optimizations for shadowsocks protocols
Feb 21, 2026
2f64a6b
perf(shadowsocks): integrate UDP cipher cache optimization with 5x+ p…
Feb 21, 2026
b663b37
feat(trojan): optimize password hash with sync.Map cache
Feb 21, 2026
af16289
fix(ss2022): add ParseMagicNetwork support to handle magic network fo…
Feb 23, 2026
820ca0c
fix(reality): fix nil ecdheKey error with utls v1.8.2 compatibility
Feb 23, 2026
6562617
perf(shadowsocks_2022): simplify UDP connection handling by removing …
Feb 24, 2026
2b866ff
fix(ss2022): restore magic network parsing in ListenPacket to preserv…
Feb 24, 2026
7c04ff4
fix(ss2022): handle network types with ip version and dns suffix (udp…
Feb 24, 2026
1664fec
perf(ss2022): optimize multi-PSK UDP identity header generation
Feb 24, 2026
a197d2b
Optimize UDP connection handling and cipher caching
Feb 25, 2026
a33b253
perf: migrate to olicesx/quic-go dependency
Feb 25, 2026
da115cd
chore: update quic-go to v0.0.0-20260225054405-33005db9cba0
Feb 25, 2026
b53687e
perf: enhance error handling for UDP connections and add IsTemporaryE…
Feb 25, 2026
d8e7cd8
perf: fix ListenPacket method to correctly handle network parameter f…
Feb 25, 2026
f99a240
chore: update quic-go to v0.0.0-20260226044315-bb65418d151a
Feb 26, 2026
58fcbfe
perf: optimize hot path error checking with direct comparison (3-102x…
Feb 26, 2026
adfc5fa
Refactor code for performance improvements and consistency
Feb 27, 2026
c8ead0d
perf: enhance splice operations with error handling and add unit test…
Feb 27, 2026
6653a8d
perf: implement zero-copy splice functionality for Linux and no-op fo…
Feb 28, 2026
001b559
fix: remove method overrides in FakeNetPacketConn to match netproxy.P…
Feb 28, 2026
4a2fd15
feat: add test interface to verify FakeNetPacketConn implementation o…
Feb 28, 2026
a7a5c72
chore: remove unused test interface for FakeNetPacketConn
Feb 28, 2026
40348ab
Refactor Shadowsocks encryption and decryption methods
Mar 1, 2026
4983ab6
netproxy: harden splice fallback and arch compatibility
Mar 3, 2026
b35d804
refactor: streamline splice operations and enhance syscallConn interface
Mar 4, 2026
d2266f7
fix(pool): apply strict capacity boundaries and slice bounds to preve…
Mar 4, 2026
4a22a97
fix(pool,tcp): properly handle TCP Relay unblocking and strictly reje…
Mar 4, 2026
b2b0c45
refactor(shadowsocks): optimize TCP and UDP connection handling and m…
Mar 4, 2026
545d5c1
feat(relay): expose transparent wrapper capabilities
Mar 6, 2026
5b79078
perf(ws): stream websocket frames without whole-message buffers
Mar 6, 2026
ba72efd
perf(tls): reduce fragment write allocations
Mar 6, 2026
3ffead2
perf(trojan): avoid copying first payload into header buffer
Mar 6, 2026
5854cd7
Fix outbound concurrency and add wrapper regressions
Mar 7, 2026
961252d
perf(shadowsocks): optimize UDP connection handling and remove caching
Mar 10, 2026
becc8d7
Revert "perf(shadowsocks): optimize UDP connection handling and remov…
Mar 10, 2026
f3fd8bd
refactor(ss2022): unify core logic and simplify udp cipher path
Mar 10, 2026
6720c9c
perf(ss2022): lazy-init udp session cipher for lower conn overhead
Mar 10, 2026
13b4982
feat(netproxy): add UnwrapTCPConn and related tests for TCP connectio…
Mar 11, 2026
569c788
perf(ss2022): share profile across conns
Mar 11, 2026
1a92216
perf(shadowsocks): optimize UDP connection decryption handling and ad…
Mar 11, 2026
f8ffb17
feat(dialer): add protocol-aware sticky IP caching for proxy servers
Mar 11, 2026
9fae0b9
fix(stickyip): improve UDP connectivity verification
Mar 11, 2026
7570fc8
feat(stickyip): add InvalidateProtocolCache method for immediate IP f…
Mar 11, 2026
402f159
perf(dialer): enhance logging by changing Info to Debug level for bet…
Mar 12, 2026
60178be
feat(stickyip): enhance proxy IP caching with support for IPv4 and IP…
Mar 13, 2026
e895fd2
feat(shadowsocks): optimize UDP encryption with in-place method and a…
Mar 17, 2026
f863819
Add race condition tests and benchmarks for UDP connections in shadow…
Mar 17, 2026
fcce9f9
feat(pool): implement GetBiggerClosestN function and add comprehensiv…
Mar 17, 2026
ded2d87
Merge pull request #1 from olicesx/perf/complete-optimizations
cubatic45 Mar 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions ciphers/aead_2022_cipher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package ciphers

import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
"sync"
"time"
)

type CipherConf2022 struct {
KeyLen int
SaltLen int
NonceLen int
TagLen int
NewCipher func(key []byte) (cipher.AEAD, error)
NewBlockCipher func(key []byte) (cipher.Block, error)
}

const (
// Timestamp tolerance
TimestampTolerance = 30 * time.Second

// Salt storage duration
SaltStorageDuration = 60 * time.Second
)

var (
Aead2022CiphersConf = map[string]*CipherConf2022{
"2022-blake3-aes-256-gcm": {KeyLen: 32, SaltLen: 32, NonceLen: 12, TagLen: 16, NewCipher: NewGcm, NewBlockCipher: aes.NewCipher},
"2022-blake3-aes-128-gcm": {KeyLen: 16, SaltLen: 16, NonceLen: 12, TagLen: 16, NewCipher: NewGcm, NewBlockCipher: aes.NewCipher},
}
)

// ValidateBase64PSK validates that the PSK is a valid base64 string with correct length
func ValidateBase64PSK(pskBase64 string, expectedKeyLen int) ([]byte, error) {
if pskBase64 == "" {
return nil, fmt.Errorf("PSK cannot be empty for SIP022 methods")
}

psk, err := base64.StdEncoding.DecodeString(pskBase64)
if err != nil {
return nil, fmt.Errorf("PSK must be valid base64 for SIP022 methods: %w", err)
}

if len(psk) != expectedKeyLen {
return nil, fmt.Errorf("PSK length must be %d bytes for this method, got %d", expectedKeyLen, len(psk))
}

return psk, nil
}

// SlidingWindowFilter implements a sliding window filter for packet ID replay protection
type SlidingWindowFilter struct {
window []uint64
windowSize uint64
latest uint64
initialized bool
mutex sync.Mutex
}

// NewSlidingWindowFilter creates a new sliding window filter
func NewSlidingWindowFilter(windowSize int) *SlidingWindowFilter {
if windowSize <= 0 {
windowSize = 1024
}
wordCount := (windowSize + 63) / 64
return &SlidingWindowFilter{
window: make([]uint64, wordCount),
windowSize: uint64(windowSize),
}
}

// CheckAndUpdate checks if the packet ID is valid and updates the window
func (f *SlidingWindowFilter) CheckAndUpdate(packetID uint64) bool {
f.mutex.Lock()
defer f.mutex.Unlock()
if !f.initialized {
f.initialized = true
f.latest = packetID
f.setBit(0)
return true
}

if packetID > f.latest {
shift := packetID - f.latest
f.shiftWindow(shift)
f.latest = packetID
f.setBit(0)
return true
}

distance := f.latest - packetID
if distance >= f.windowSize {
return false
}
if f.getBit(distance) {
return false
}
f.setBit(distance)
return true
}

func (f *SlidingWindowFilter) getBit(index uint64) bool {
wordIndex := index / 64
bitIndex := index % 64
return f.window[wordIndex]&(uint64(1)<<bitIndex) != 0
}

func (f *SlidingWindowFilter) setBit(index uint64) {
wordIndex := index / 64
bitIndex := index % 64
f.window[wordIndex] |= uint64(1) << bitIndex
}

func (f *SlidingWindowFilter) setBitInWindow(window []uint64, index uint64) {
wordIndex := index / 64
bitIndex := index % 64
window[wordIndex] |= uint64(1) << bitIndex
}

func (f *SlidingWindowFilter) shiftWindow(shift uint64) {
if shift >= f.windowSize {
// Clear all bits in-place
for i := range f.window {
f.window[i] = 0
}
return
}

// Optimized in-place shift to avoid allocation
wordShift := int(shift / 64)
bitShift := shift % 64

// Shift right by wordShift positions
if wordShift > 0 {
for i := len(f.window) - 1; i >= wordShift; i-- {
f.window[i] = f.window[i-wordShift]
}
for i := 0; i < wordShift; i++ {
f.window[i] = 0
}
}

// Handle remaining bit shift
if bitShift > 0 {
for i := len(f.window) - 1; i > 0; i-- {
f.window[i] = (f.window[i] >> bitShift) | (f.window[i-1] << (64 - bitShift))
}
f.window[0] = f.window[0] >> bitShift
}
}
46 changes: 46 additions & 0 deletions ciphers/aead_2022_cipher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ciphers

import "testing"

func TestSlidingWindowFilter_BasicAndDuplicate(t *testing.T) {
f := NewSlidingWindowFilter(64)

if !f.CheckAndUpdate(100) {
t.Fatalf("first packet should pass")
}
if f.CheckAndUpdate(100) {
t.Fatalf("duplicate packet should be rejected")
}
if !f.CheckAndUpdate(101) {
t.Fatalf("next packet should pass")
}
if !f.CheckAndUpdate(99) {
t.Fatalf("out-of-order but in-window packet should pass")
}
if f.CheckAndUpdate(99) {
t.Fatalf("duplicate out-of-order packet should be rejected")
}
}

func TestSlidingWindowFilter_ShiftAndTooOld(t *testing.T) {
f := NewSlidingWindowFilter(8)

if !f.CheckAndUpdate(1) {
t.Fatalf("packet 1 should pass")
}
if !f.CheckAndUpdate(2) {
t.Fatalf("packet 2 should pass")
}
if !f.CheckAndUpdate(20) {
t.Fatalf("packet 20 should pass")
}
if f.CheckAndUpdate(2) {
t.Fatalf("packet 2 should be too old after large shift")
}
if !f.CheckAndUpdate(19) {
t.Fatalf("packet 19 should pass within current window")
}
if f.CheckAndUpdate(19) {
t.Fatalf("duplicate packet 19 should be rejected")
}
}
9 changes: 5 additions & 4 deletions ciphers/aead_cipher.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ var (
"aes-256-gcm": {KeyLen: 32, SaltLen: 32, NonceLen: 12, TagLen: 16, NewCipher: NewGcm},
"aes-128-gcm": {KeyLen: 16, SaltLen: 16, NonceLen: 12, TagLen: 16, NewCipher: NewGcm},
}
ZeroNonce [MaxNonceSize]byte
ShadowsocksReusedInfo = []byte("ss-subkey")
JuicityReusedInfo = []byte("juicity-reused-info")
ZeroNonce [MaxNonceSize]byte
JuicityReusedInfo = []byte("juicity-reused-info")
)

func NewGcm(key []byte) (cipher.AEAD, error) {
Expand All @@ -46,7 +45,9 @@ func NewGcm(key []byte) (cipher.AEAD, error) {
return cipher.NewGCM(block)
}

// Verify is used for legacy compatibility
func (conf *CipherConf) Verify(buf []byte, masterKey []byte, salt []byte, cipherText []byte, subKey *[]byte) ([]byte, bool) {
var shadowsocksReusedInfo = []byte("ss-subkey")
var sk []byte
if subKey != nil && len(*subKey) == conf.KeyLen {
sk = *subKey
Expand All @@ -57,7 +58,7 @@ func (conf *CipherConf) Verify(buf []byte, masterKey []byte, salt []byte, cipher
sha1.New,
masterKey,
salt,
ShadowsocksReusedInfo,
shadowsocksReusedInfo,
)
io.ReadFull(kdf, sk)
if subKey != nil && cap(*subKey) >= conf.KeyLen {
Expand Down
Loading