RealityClient
RealityClient tunnel سمت client برای Reality v2 است. هر line با TLS handshake واقعی از طریق TlsClient داخلی آغاز میشود و material مستقل هر جهت از همان handshake مشتق میگردد. در TLS 1.3، پیش از takeover خام یک handoff احرازشده request/ack/confirm انجام میشود؛ TLS 1.2 مسیر فوری و profile-aware فعلی را حفظ میکند.
زمانی از این نود استفاده کنید که بایتهای ابتدایی روی wire باید برای یک visitor domain شبیه نشست عادی TLS client به نظر برسند، در حالی که payload اصلی واتروال با secret مشترک Reality محافظت میشود.
جایگاه رایج
TcpListener -> RealityClient -> TcpConnector
نود next مسیر transport به سمت RealityServer را فراهم میکند. RealityClient خودش socket مقصد را باز نمیکند؛ بنابراین یک connector یا transport دیگر باید پس از آن قرار بگیرد.
نمونه تنظیمات
{
"name": "reality-client",
"type": "RealityClient",
"settings": {
"sni": "www.example.com",
"verify": true,
"password": "replace-with-a-strong-secret",
"algorithm": "chacha20-poly1305"
},
"next": "server-tcp"
}
با transport TCP معمولی:
{
"name": "server-tcp",
"type": "TcpConnector",
"settings": {
"address": "203.0.113.10",
"port": 443
}
}
فیلدهای اجباری
| فیلد | نوع | توضیح |
|---|---|---|
type | string | باید RealityClient باشد. |
next | string | نودی که stream مربوط به TLS و Reality recordها را به سمت server حمل میکند. |
settings | object | باید object غیرخالی باشد. |
settings.sni | string | برای TlsClient داخلی الزامی است و نام TLS server مربوط به visitor را مشخص میکند. |
settings.password | string | secret مشترک Reality شامل ۱ تا ۳۲ بایت UTF-8؛ باید با server یکسان باشد. |
تنظیمات
RealityClient همان object تنظیمات را به TlsClient داخلی میدهد؛ بنابراین رفتار گزینههای مرتبط با TLS مانند sni، verify، ech-sni-trick، x25519mlkem768 و verbose تابع TlsClient است.
| گزینه | پیشفرض | توضیح |
|---|---|---|
sni | اجباری | SNI مربوط به visitor domain که توسط TlsClient داخلی استفاده میشود. |
verify | true | مقدار boolean مربوط به certificate verification برای TlsClient داخلی. |
ech-sni-trick | تنظیم نشده | string غیرخالی اختیاری که به TlsClient داخلی پاس داده میشود. |
x25519mlkem768 | true | مقدار boolean برای فعال/غیرفعال کردن hybrid TLS group در TlsClient داخلی. |
verbose | false | مقدار boolean برای فعالکردن log بیشتر از state داخلی TLS. |
password | اجباری | secret مشترک شامل ۱ تا ۳۲ بایت UTF-8 برای key derivation. |
algorithm | chacha20-poly1305 | یکی از chacha20-poly1305، chacha20poly1305، aes-gcm، aes-256-gcm یا aes256gcm. |
method | مثل algorithm | alias برای algorithm، وقتی algorithm وجود نداشته باشد. |
salt | waterwall-reality | salt برای key derivation. مقدار صریح باید شامل ۱ تا ۳۲ بایت UTF-8 باشد و با server یکی باشد. |
kdf-iterations | 12000 | تعداد integer roundهای key derivation در بازه 1 تا 1000000؛ عدد اعشاری رد میشود. |
اگر aes-gcm انتخاب شود اما crypto backend فعال از AES-GCM پشتیبانی نکند، ساخت tunnel شکست میخورد.
محدودیت credential برحسب بایت UTF-8 سنجیده میشود، نه تعداد character، و همان طولی است که به BLAKE2s داده میشود. مقدار پیشفرض فقط وقتی اعمال میشود که key اختیاری وجود نداشته باشد؛ key موجود با نوع JSON اشتباه خطای startup است. اگر algorithm و method هر دو وجود داشته باشند، algorithm اولویت دارد و alias معتبر نمیتواند primary نامعتبر را نجات دهد. keyهای ناشناخته همچنان پذیرفته میشوند، چون این object با TlsClient داخلی مشترک است.
جریان اجرا
در upstream Init، RealityClient:
- per-line state خودش را مقداردهی اولیه میکند
Initرا بهTlsClientداخلی میفرستد- منتظر کامل شدن TLS handshake داخلی میماند
payloadهای upstream تا پایان takeover در صف میمانند. با کامل شدن handshake داخلی، RealityClient نسخه TLS، IANA cipher suite، client random، server random و sequenceهای بعدی read/write در TLS 1.2 را در حالی که SSL object هنوز موجود است ثبت میکند. سپس record profile مشترک را انتخاب میکند و key/IV مستقل client-to-server و server-to-client را از binding ثبتشده و root key مشتقشده از password میسازد. cipher suite پشتیبانینشده بهجای استفاده از یک layout عمومی، takeover را متوقف میکند.
TLS 1.2 سپس state داخلی TLS را فوراً آزاد میکند و counterها و layoutهای record ثبتشده را ادامه میدهد. در TLS 1.3، BoringSSL حفظ میشود و HANDOFF_REQUEST با sequence صفر client-to-server ارسال میگردد. تا رسیدن HANDOFF_ACK، هر record کامل فقط روی یک duplicate بهعنوان ACK مورد انتظار آزمایش میشود. شکست این trial نه sequence را جلو میبرد و نه record اصلی را تغییر میدهد؛ record اصلی برای پردازش عادی NewSessionTicket، KeyUpdate، application-data یا alert مربوط به cover به BoringSSL داده میشود. protocol output تولیدشده پیش از control بعدی upstream میرود. پس از ACK با sequence صفر server-to-client، client BoringSSL را آزاد میکند، HANDOFF_CONFIRM را با sequence یک client-to-server میفرستد، downstream Est را اعلام میکند و payloadهای صفشده را از sequence دو آغاز میکند.
بعد از takeover:
| جهت | رفتار |
|---|---|
| Upstream payload | cleartext نود قبلی رمزنگاری و در قالب recordهای شبیه TLS application-data به next فرستاده میشود. |
| Downstream payload | recordهای Reality از next parse، احراز اصالت و رمزگشایی میشوند و cleartext به نود قبلی میرود. |
پیش از ACK احرازشده TLS 1.3، خطای cover TLS، finish محلی یا ورودی malformed بدون alert مصنوعی Reality بسته میشود، چون peer ممکن است هنوز stream را TLS واقعی بداند. پس از handoff، framing نامعتبر، شکست authentication یا semantic احرازشده نامعتبر، در صورت writable بودن سمت wire حداکثر یک alert محافظتشده fatal از نوع bad_record_mac میفرستد و سپس هر دو جهت را میبندد.
سیاست shutdown به نقش هر سمت وابسته است. Finish محلی upstream پس از پاککردن state مربوط به Reality فقط next را میبندد و هرگز close_notify مصنوعی تولید نمیکند. close_notify احرازشده server بدون پاسخ مصرف میشود؛ سپس RealityClient فوراً state خودش و هر دو سمت باز را میبندد. client منتظر Finish بعدی TCP نمیماند، بنابراین peer احرازشده نمیتواند با نفرستادن FIN connection را باز نگه دارد. تنها مسیر تولید alert در client، تشخیص محلی خطای record احرازشده است که حداکثر یک fatal bad_record_mac میفرستد و سپس هر دو سمت را میبندد. fatal alert دریافتی نیز بدون پاسخ فوراً هر دو سمت را میبندد. Finish خام transport فقط سمت محلی باقیمانده را میبندد. این رفتار setting جدیدی ندارد.
قالب Record
شکل visible هر Reality record بر اساس cipher suite واقعی TLS انتخاب میشود:
| cover مذاکرهشده | body قابلمشاهده Reality |
|---|---|
| TLS 1.3 | Reality-AEAD(payload + encrypted inner type 0x17) + 16-byte tag؛ بدون nonce/prefix قابلمشاهده، با طول body برابر P + 17 و حداکثر 16401. |
| TLS 1.2 ChaCha20-Poly1305 | Reality-AEAD(payload) + 16-byte tag؛ بدون nonce/prefix قابلمشاهده، با طول body برابر P + 16 و حداکثر 16400. |
| TLS 1.2 AES-GCM | 8-byte explicit nonce + Reality ciphertext + 16-byte tag؛ حداکثر body برابر 16408. |
| TLS 1.2 AES-CBC/SHA-1 | 16-byte random explicit IV + block-aligned opaque body؛ حداکثر body برابر 16432. |
در GCM، explicit nonce جهت client-to-server ادامه sequence واقعی ثبتشده BoringSSL است. در CBC، inner plaintext احرازشده شامل طول دو بایتی payload و zero filler است تا طول visible با فرمول MAC و minimum padding در TLS سازگار باشد؛ Reality عملیات CBC encryption انجام نمیدهد.
TLS-like header از این مقادیر استفاده میکند:
| فیلد | مقدار |
|---|---|
| Content type | 0x17 application data |
| Version | 0x0303 |
| Length | visible prefix پروفایل انتخابشده بهعلاوه opaque body احرازشده |
visible prefix احراز اصالت میشود اما AEAD nonce نیست. Reality sequence مستقل هر جهت از صفر آغاز میشود و nonce از IV محرمانه همان جهت ساخته میشود. TLS facade sequence در GCM مستقل از Reality replay sequence نگه داشته میشود.
associated data شامل profile ID، domain مربوط به v2، جهت، Reality sequence، session ID، TLS header، طول visible prefix و خود prefix است. بنابراین replay، reorder، reflection، cross-profile و cross-connection شکست میخورند و counterها پیش از wrap بسته میشوند.
alertهای کنترلی شکل public سازگار با BoringSSL دارند:
| cover مذاکرهشده | header و body مربوط به alert |
|---|---|
| TLS 1.3 | outer type برابر 0x17 و body برابر 19 بایت؛ plaintext رمزشده شامل alert دو بایتی و inner type برابر 0x15 است. |
| TLS 1.2 AES-GCM | outer type برابر 0x15 و body برابر 26 بایت با explicit nonce هشتبایتی. |
| TLS 1.2 AES-CBC/SHA-1 | outer type برابر 0x15 و body برابر 48 بایت با IV تازه 16 بایتی. |
| TLS 1.2 ChaCha20-Poly1305 | outer type برابر 0x15 و body برابر 18 بایت. |
مقادیر semantic رمزشده 01 00 برای close_notify و 02 14 برای fatal bad_record_mac هستند. data و alert در هر جهت یک sequence مشترک دارند. record kind و نسخه TLS نیز وارد associated data میشوند تا تبدیل data به alert یا برعکس معتبر نباشد. اینها recordهای camouflage محافظتشده با Reality AEAD هستند، نه TLS closure واقعی یا alert رمزشده با keyهای TLS.
سه control مربوط به handoff در TLS 1.3 از outer type و inner type برابر application-data استفاده میکنند. plaintext نسخهدار آنها شامل control code مطابق AAD و padding احرازشده CSPRNG بر اساس policy مشترک طول عمومی است. طول body در TLS بهطور یکنواخت از بازه 22..1172 byte نمونهبرداری میشود (3..1153 byte padding)؛ این بازه در مسیرهای post-handshake تحت پوشش BoringSSL مشاهده شده است. padding به application داده نمیشود. request، ACK، confirm، data و alert یک sequence سختگیرانه مشترک در هر جهت دارند و قابل جایگزینی نیستند. setting JSON برای طول control وجود ندارد.
نکتههای Buffer و Lifecycle
RealityClientبهازای هر line یک state دارد که شامل parser مربوط به recordهای downstream، payloadهای upstream صفشده پیش از handshake، session ID، keyها و IVهای هر جهت و counterهای ارسال و دریافت است.- این نود line معمولی را نه ایجاد میکند و نه از بین میبرد؛ هنگام
Finishیا زمانی که بهدلیل خطای Reality هر دو جهت را میبندد، فقط state خودش را پاک میکند. - پیش از فرستادن fatal alert تولیدشده بهصورت محلی، state وارد حالت terminal میشود؛ callbackهای re-entrant مربوط به Payload/Pause/Resume/Est بازتاب داده نمیشوند، مسیر ارسال مالک پاکسازی state در صورت مرگ line است و teardown اضافه متوقف میشود.
- این نود
21بایت required left padding اعلام میکند:5بایت header و حداکثر16بایت explicit IV مربوط به CBC. - buffer خالی payload بدون فرستادن Reality record به pool برگردانده میشود.
- حداکثر plaintext هر fragment بهصورت خودکار برابر limit بومی TLS یعنی
16384بایت است. callbackهای بزرگتر فوراً split میشوند؛32768بایت دقیقاً دو record کامل میسازد و buffering بین callbackها وجود ندارد.
مدل تهدید و محدودیتهای امنیتی
مدل تهدید بررسیشده برای camouflage یک ناظر در مسیر است که TLS را terminate نمیکند. این ناظر میتواند stream کامل دوطرفه را capture و reassemble کند؛ type، version، length، direction، ordering و timing مربوط به TLS recordها، nonce/IVهای قابلمشاهده و رفتار close را ببیند؛ bytes را دستکاری کند؛ و با آگاهی از implementation اتصال probe ایجاد کند. فرض میشود ناظر password مربوط به Reality یا traffic secretهای TLS واقعی را نمیداند، TLS را terminate نمیکند و cover destination تنظیمشده را کنترل نمیکند یا با آن تبانی ندارد. همچنین نمیتواند handshake کاملشده را بدون شکست TLS Finished verification تغییر دهد.
تازگی material هر نشست Reality به client random و server random تازه در handshake واقعی TLS وابسته است. Reality هیچ server challenge مستقل مخصوص تازگی نشست ندارد. بنابراین محافظت در برابر replay میان connectionها فرض میکند ناظر نمیتواند هر دو handshake random را پیشبینی یا کنترل کند. cover destination مخرب یا همدستی که عمداً به بازسازی binding یک handshake قبلی کمک کند خارج از این مدل است؛ دفاع در برابر چنین مدل قویتری به server challenge مستقل و احرازشده نیاز دارد.
camouflage در Reality فقط ویژگیهای public انتخابشده TLS را بازسازی میکند؛ از جمله شکل و طول record متناسب با suite، nonce/IV صریح، controlها، alertها و رفتار close. با این حال، پس از takeover، recordها ciphertext مربوط به Reality-AEAD را حمل میکنند، نه ciphertext واقعی ساختهشده با TLS traffic keyهای اتصال cover. Reality ادعا نمیکند در برابر TLS termination پنهان میماند یا همه timing distributionها، write sizeها، burst patternها و semanticهای application در Chrome را بازسازی میکند. ناظری که TLS را terminate کند میتواند traffic keyهای واقعی را روی recordهای بعدی آزمایش کند و خارج از محدوده camouflage بررسیشده است.
نکتههای عملیاتی
RealityClientبهTlsClientوابسته است و build بایدINCLUDE_TLS_CLIENT=ONداشته باشد.- اولین مرحله یک TLS client handshake واقعی است، بنابراین
sni، رفتار certificate verification و گزینههای TLS fingerprinting اهمیت دارند. password،salt،kdf-iterationsوalgorithmباید باRealityServerیکی باشند.- handoff احرازشده TLS 1.3 با buildهای منتشرنشده قبلی که در پایان handshake اصلی takeover میکردند ناسازگار است. هر دو peer را همزمان ارتقا دهید؛ layoutهای TLS 1.2 تغییر نکردهاند، fallback به v1 یا layout قدیمی وجود ندارد و setting منسوخ
max-frame-sizeدر startup رد میشود. - مقدار مشتقشده از password یک root key است، نه record key. هر connection واقعی TLS، material مستقل هر جهت را بهصورت تازه مشتق میکند و چرخش password نشستهای آینده را بدون نیاز به replay database نامعتبر میکند.
- این نود یک stream tunnel است، نه packet tunnel.
مشخصات نود
| ویژگی | مقدار |
|---|---|
| جایگاه | Middle stream tunnel |
نیاز به next | بله |
| Previous layer | Anything |
| Next layer | Anything |
| Per-line state | دارد |
| ایجاد line | خیر |
| از بین بردن line | خیر |
| Required left padding | 21 |