Skip to content

بروتوكول Shadowsocks

يُنفّذ Xray-core جيلين من Shadowsocks: تشفيرات AEAD الكلاسيكية (Shadowsocks الأصلي) في proxy/shadowsocks/، وبروتوكول Shadowsocks 2022 الحديث (عبر مكتبة sing-shadowsocks) في proxy/shadowsocks_2022/.

نظرة عامة

الميزةSS الكلاسيكيShadowsocks 2022
واردنعمنعم (مستخدم واحد + متعدد المستخدمين + ترحيل)
صادرنعمنعم
TCPنعمنعم
UDPنعمنعم
التشفيرAES-128/256-GCM, ChaCha20-Poly1305, XChaCha20-Poly1305, بدون2022-blake3-aes-128/256-gcm, 2022-blake3-chacha20-poly1305
تعدد المستخدميننعم (AEAD فقط)نعم (بمفاتيح PSK منفصلة)
حماية إعادة التشغيلتصريف بذرة السلوكمُدمجة (مكتبة sing)
صيغة المفتاحكلمة مرور (اشتقاق MD5)مفتاح مُشترك مسبقاً بترميز base64

Shadowsocks الكلاسيكي

صيغة السلك — تدفق TCP

+---------+-------------------+-------------------+
| [IV]    | Encrypted Header  | Encrypted Payload |
| 0-32B   | (chunked AEAD)    | (chunked AEAD)    |
+---------+-------------------+-------------------+

الترويسة (داخل أول جزء مشفر):

+----------+----------+------+
| AddrType | Address  | Port |
| 1 byte   | variable | 2B   |
+----------+----------+------+

المصدر: proxy/shadowsocks/protocol.go:57-131

أنواع العناوين:

البايت (البتات السفلى 4)النوع
0x01IPv4 (4 بايت)
0x03نطاق (1 بايت للطول + سلسلة نصية)
0x04IPv6 (16 بايت)

ملاحظة: يستخدم محلل العناوين محللاً مخصصاً يُقنّع البتات العليا 4: b & 0x0F

المصدر: proxy/shadowsocks/protocol.go:24-31

صيغة السلك — حزمة UDP

كل حزمة UDP تُشفّر بشكل مستقل:

+---------+----------+----------+------+---------+----------+
| IV      | AddrType | Address  | Port | Payload | Auth Tag |
| 0-32B   | 1B       | variable | 2B   | ...     | 16B      |
+---------+----------+----------+------+---------+----------+

المصدر: proxy/shadowsocks/protocol.go:207-228

تنفيذات التشفير

تشفيرات AEAD

جميع تشفيرات AEAD تتبع النمط ذاته:

  1. IV: بايتات عشوائية (بحجم خاص بالتشفير) تُضاف في بداية التدفق
  2. اشتقاق المفتاح الفرعي: HKDF-SHA1 مع IV كملح: HKDF(key, iv, "ss-subkey") -> subkey
  3. أجزاء مُصادقة: كل جزء هو [encrypted_length(2B + 16B tag)] [encrypted_payload(N + 16B tag)]
  4. Nonce: عداد متزايد تلقائياً
go
func (c *AEADCipher) createAuthenticator(key, iv []byte) *crypto.AEADAuthenticator {
    subkey := make([]byte, c.KeyBytes)
    hkdfSHA1(key, iv, subkey)
    aead := c.AEADAuthCreator(subkey)
    nonce := crypto.GenerateAEADNonceWithSize(aead.NonceSize())
    return &crypto.AEADAuthenticator{
        AEAD:           aead,
        NonceGenerator: nonce,
    }
}

المصدر: proxy/shadowsocks/config.go:138-147

التشفيرحجم المفتاححجم IVAEAD
AES_128_GCM1616AES-128-GCM
AES_256_GCM3232AES-256-GCM
CHACHA20_POLY13053232ChaCha20-Poly1305
XCHACHA20_POLY13053232XChaCha20-Poly1305
NONE00بدون (نص عادي)

المصدر: proxy/shadowsocks/config.go:62-93

اشتقاق المفتاح من كلمة المرور

يشتق Shadowsocks الكلاسيكي مفاتيح التشفير من كلمات المرور باستخدام MD5 المتكرر:

go
func passwordToCipherKey(password []byte, keySize int32) []byte {
    key := make([]byte, 0, keySize)
    md5Sum := md5.Sum(password)
    key = append(key, md5Sum[:]...)
    for int32(len(key)) < keySize {
        md5Hash := md5.New()
        md5Hash.Write(md5Sum[:])
        md5Hash.Write(password)
        md5Hash.Sum(md5Sum[:0])
        key = append(key, md5Sum[:]...)
    }
    return key
}

المصدر: proxy/shadowsocks/config.go:213-228

اشتقاق المفتاح الفرعي بـ HKDF

go
func hkdfSHA1(secret, salt, outKey []byte) {
    r := hkdf.New(sha1.New, secret, salt, []byte("ss-subkey"))
    io.ReadFull(r, outKey)
}

المصدر: proxy/shadowsocks/config.go:230-233

دعم تعدد المستخدمين

يتكرر Validator على جميع المستخدمين المسجلين، محاولاً فك تشفير AEAD بمفتاح كل مستخدم:

go
func (v *Validator) Get(bs []byte, command RequestCommand) (...) {
    for _, user := range v.users {
        account := user.Account.(*MemoryAccount)
        if account.Cipher.IsAEAD() {
            aeadCipher := account.Cipher.(*AEADCipher)
            iv := bs[:ivLen]
            subkey := hkdfSHA1(account.Key, iv, ...)
            aead := aeadCipher.AEADAuthCreator(subkey)
            // Try to decrypt first chunk
            ret, matchErr = aead.Open(data[:0], nonce, bs[ivLen:ivLen+18], nil)
            if matchErr == nil { return user }
        }
    }
}

المصدر: proxy/shadowsocks/validator.go:112-154

القيد: تشفيرات غير AEAD (None) تدعم مستخدماً واحداً فقط لأنه لا يوجد وسم مصادقة للمطابقة.

المصدر: proxy/shadowsocks/validator.go:33-35

تصريف بذرة السلوك

مثل VMess، يستخدم Shadowsocks مُصرّفاً حتمياً لقراءة كميات عشوائية من البيانات قبل إغلاق الاتصالات غير الصالحة، مما يمنع الاستقصاء:

go
hashkdf := hmac.New(sha256.New, []byte("SSBSKDF"))
hashkdf.Write(account.Key)
behaviorSeed = crc64.Update(behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil))

المصدر: proxy/shadowsocks/validator.go:39-41

Shadowsocks 2022

Shadowsocks 2022 هو إعادة تصميم كاملة بخصائص أمنية أفضل. يفوّض Xray-core تنفيذ البروتوكول إلى مكتبة sing-shadowsocks (github.com/sagernet/sing-shadowsocks).

الاختلافات الرئيسية عن الإصدار الكلاسيكي

  1. مفاتيح مُشتركة مسبقاً بدلاً من كلمات المرور — المفاتيح مُرمّزة بـ base64 ويجب أن تتطابق مع حجم مفتاح التشفير بالضبط
  2. حماية إعادة التشغيل مُدمجة في البروتوكول (مبنية على الطابع الزمني)
  3. فصل تشفير الترويسة والحمولة بقيم nonce مختلفة
  4. تعدد المستخدمين يستخدم PSK على مستوى الخادم + PSK لكل مستخدم (EIH — ترويسة الهوية المشفرة)

الأساليب المدعومة

الأساليب المتاحة تأتي من shadowaead_2022.List:

  • 2022-blake3-aes-128-gcm
  • 2022-blake3-aes-256-gcm
  • 2022-blake3-chacha20-poly1305

الوارد أحادي المستخدم

الملف: proxy/shadowsocks_2022/inbound.go

go
service, err := shadowaead_2022.NewServiceWithPassword(config.Method, config.Key, 500, inbound, nil)

المصدر: proxy/shadowsocks_2022/inbound.go:55-58

تتولى الخدمة:

  • TCP: service.NewConnection(ctx, connection, metadata)
  • UDP: service.NewPacket(ctx, pc, packet, metadata)

الوارد متعدد المستخدمين

الملف: proxy/shadowsocks_2022/inbound_multi.go

يستخدم shadowaead_2022.NewMultiService[int] مع PSK خادم (مُرمّز بـ base64) وكلمات مرور لكل مستخدم:

go
service, err := shadowaead_2022.NewMultiService[int](config.Method, psk, 500, inbound, nil)
service.UpdateUsersWithPasswords(indices, passwords)

المصدر: proxy/shadowsocks_2022/inbound_multi.go:76-86

يتم تحديد هوية المستخدم عبر آلية EIH (ترويسة الهوية المشفرة) الداخلية لمكتبة sing.

الصادر

الملف: proxy/shadowsocks_2022/outbound.go

go
method, err := shadowaead_2022.NewWithPassword(config.Method, config.Key, nil)
// TCP
serverConn := o.method.DialEarlyConn(connection, singbridge.ToSocksaddr(destination))
// UDP
serverConn := o.method.DialPacketConn(connection)

المصدر: proxy/shadowsocks_2022/outbound.go:47-57، proxy/shadowsocks_2022/outbound.go:98-155

UDP عبر TCP (UoT)

يدعم الصادر نقل UDP عبر TCP عند تفعيل UdpOverTcp:

go
if config.UdpOverTcp {
    o.uotClient = &uot.Client{Version: uint8(config.UdpOverTcpVersion)}
}

المصدر: proxy/shadowsocks_2022/outbound.go:58-60

المعالج الوارد (خادم SS الكلاسيكي)

الملف: proxy/shadowsocks/server.go

يتعامل الخادم مع TCP وUDP:

go
func (s *Server) Network() []net.Network {
    list := s.config.Network
    if len(list) == 0 {
        list = append(list, net.Network_TCP)
    }
    return list
}

المصدر: proxy/shadowsocks/server.go:81-87

تدفق TCP: قراءة الترويسة المشفرة، مطابقة المستخدم عبر Validator.Get()، فك تشفير العنوان، التوزيع.

تدفق UDP: كل حزمة UDP تُشفّر بشكل مستقل. يفك الخادم تشفير كل حزمة، يستخرج الوجهة، ويوزّع عبر udp.Dispatcher.

المعالج الصادر (عميل SS الكلاسيكي)

الملف: proxy/shadowsocks/client.go

لـ TCP:

  1. توليد IV عشوائي، كتابته إلى الاتصال
  2. إنشاء كاتب تشفير عبر account.Cipher.NewEncryptionWriter()
  3. كتابة ترويسة عنوان بأسلوب SOCKS5
  4. بث الحمولة عبر كاتب التشفير

لـ UDP:

  1. كل حزمة صادرة تُشفّر بشكل مستقل بـ EncodeUDPPacket()
  2. حزم الاستجابة تُفك تشفيرها بـ DecodeUDPPacket()

المصدر: proxy/shadowsocks/client.go:48-195

ملاحظات التنفيذ

  1. إهمال تشفيرات التدفق: يوجد نوع NoneCipher للتوافق مع الإصدارات السابقة لكنه لا يوفر تشفيراً. تمت إزالة تشفيرات التدفق (RC4, ChaCha20 بدون Poly1305) بالكامل.

  2. تفرّد IV: رغم وجود نوع خطأ ErrIVNotUnique، فإن فحص IV معطّل في المُدقق (validator.go:148). يعتمد بروتوكول Shadowsocks الكلاسيكي في Xray على وسم AEAD للمصادقة بدلاً من تتبع IV.

  3. تقنيع نوع العنوان: يُقنّع محلل عناوين Shadowsocks البتات العليا 4 من بايت نوع العنوان (b & 0x0F)، مما يسمح بتضمين أعلام إضافية في البتات العليا.

  4. Cone NAT: يدعم كلا الخادمين الكلاسيكي و2022 وضع "cone" لـ UDP. عند التفعيل، تُعيد الحزم اللاحقة من نفس العميل استخدام الوجهة الأولى للتوزيع، محاكيةً سلوك NAT.

  5. وضع الترحيل: يحتوي Shadowsocks 2022 على وارد ترحيل (inbound_relay.go) يسمح بسلاسل ترحيل متعددة القفزات، رغم أن هذا استخدام أكثر تخصصاً.

  6. sing-bridge: يستخدم كود Shadowsocks 2022 مساعدات singbridge للتحويل بين أنواع مكتبة sing (مثل M.Socksaddr) وأنواع Xray-core الداخلية (مثل net.Destination).

تحليل تقني لأغراض إعادة التنفيذ.