کتابخانه‌ی استاندارد · شبکه

اثر انگشت تی ال اس net.utls

اثر انگشت پیام آغازین TLS: اولین پیام دست‌دهی TLS 1.3 را با چینش بایت کروم، فایرفاکس یا سافاری بسازید، رشته‌ی JA3 حساب کنید و از آن دست‌دهی را اجرا کنید.

واردسازی net.utls

نام آرگومان‌ها شناسه‌ی خود کد است و معادل فارسی ندارد؛ گونه‌ها، کلیدواژه‌ها و نام بسته‌ها با معادل فارسی نوشته می‌شوند.

دیوار آتشی که ترافیک شما را نمی‌خواند، هنوز می‌تواند پیام اول را بخواند. ClientHello یک اتصال TLS رمزها، افزونه‌ها و ترتیبشان، منحنی‌ها و الگوریتم‌های امضایی را که کارخواه پشتیبانی می‌کند فهرست می‌کند، و هر کتابخانه‌ی TLS این فهرست را به شکل خودش می‌نویسد. این فهرست یک اثر انگشت است (JA3 شناخته‌شده‌ترین راه نوشتنش است) و کارخواه پراکسی که فهرست پیش‌فرض کتابخانه‌اش را به کار ببرد اثر انگشتی دارد که هیچ مرورگری ندارد. این بسته خودِ ClientHello را از روی توصیفی که می‌توانید بخوانید و عوض کنید می‌سازد تا چینش یک مرورگر را داشته باشد.

نمایه یک Spec برای یک خانواده‌ی کارخواه برمی‌گرداند. ساخت یک مشخصه و چند Options (نام سرور، فهرست ALPN، بر زدن افزونه‌ها) را به یک Hello تبدیل می‌کند: بایت‌های پیام با عدد تصادفی تازه، جفت کلید X25519 تازه و شناسه‌ی نشست تصادفی. پوشش کارخواه سپس روی سوکت وصل‌شده با آن پیام دست‌دهی TLS 1.3 را اجرا می‌کند و یک tls.Conn معمولی برمی‌گرداند. جی ای سه اثر انگشت هر پیامی را می‌نویسد و رکورد آن را در یک رکورد TLS می‌پیچد، مثلاً برای دادن به ابزار تکه‌تکه‌کردن سانسورشکن. ریالیتی این بسته را برای پیامی که اثبات خود را در آن مهر می‌کند به کار می‌برد.

نمایه‌ها

نامقرار است شبیه چه باشد
chrome، edgeیک کرومیوم جدید: مقدارهای GREASE، سهم کلید ترکیبی پساکوانتومی، GREASE برای ECH، افزونه‌ی ALPS، فشرده‌سازی گواهی و پر کردن تا ۵۱۲ بایت در صورت نیاز.
chrome_120نمایه‌ی قدیمی: کرومیومی از پیش از سهم کلید ترکیبی، بدون گروه پساکوانتومی. ایکس‌ری ۲۶.۹.۹ و بعدتر (ریالیتی از ۲۰۲۶-۰۹-۰۸) پیامی را که X25519MLKEM768 ندارد رها می‌کنند، پس فقط برای کارسازهای قدیمی‌تر به کار ببرید (با ایکس‌ری ۲۶.۷.۲۸ احراز می‌شود). در نام نمایه‌ها نیست؛ نام نمایه‌های قدیمی آن را می‌آورد.
firefoxفایرفاکس: بدون GREASE، رمزها به ترتیب خودش، سهم کلید secp256r1 کنار X25519، اعتبارنامه‌های تفویضی و حد اندازه‌ی رکورد.
safari، iosسافاری و iOS: GREASE، فهرست بلندتر رمزهای قدیمی‌تر، بدون ECH.
randomizedچینش کروم با افزونه‌ها و رمزهای برزده‌شده تا هیچ دو پیامی JA3 یکسان نداشته باشند.

این‌ها تقریب‌هایی هستند که از حافظه‌ی آنچه این کارخواه‌ها می‌فرستند نوشته شده‌اند، نه ضبط واقعی. مقایسه با ضبط مرورگر واقعی اینجا ممکن نبود. ساختار به دو راه سنجیده شد: پیام‌ها با net.censor.ParseClientHello خوانده می‌شوند و با سرور بسته‌ی tls دست‌دهی TLS 1.3 واقعی را کامل می‌کنند. مرورگرها با هر انتشار پیامشان را عوض می‌کنند، پس نمایه را نقطه‌ی شروع بدانید و Spec را (فهرست‌های متنی ساده است) مطابق مرورگر دلخواه ویرایش کنید.

مشخصه چگونه نوشته می‌شود

فهرست‌های Spec متنی از عددهای شانزدهی جداشده با فاصله‌اند. g0 تا g4 جایگاه‌های GREASE هستند که به یک مقدار رزروشده‌ی تصادفی (0a0a، 1a1a و ...) تبدیل می‌شوند؛ pad جای افزونه‌ی پرکننده است. extensions ترتیب روی سیم است؛ 0000 نام سرور، 0010 ALPN، 0033 سهم کلید، fe0d GREASE برای ECH و ... . key_shares می‌گوید کدام سهم‌ها فرستاده شود: 001d X25519 با جفت کلید همان پیام، 0017 سهم secp256r1 با بایت تصادفی و 11ec X25519MLKEM768.

چیزهایی که گیر می‌اندازند

  • سهم‌های کلید غیر از X25519 کلیدی پشتشان نیست. سهم secp256r1 بایت تصادفی است و بخش ML-KEM سهم ترکیبی یک کلید کپسوله‌سازی تصادفیِ خوش‌ساخت است. سروری که یکی از آن گروه‌ها را برگزیند نمی‌تواند دست‌دهی را با شما تمام کند؛ برای X25519 می‌تواند. با pq = false گروه ترکیبی را کلاً از پیام کنار بگذارید.
  • پیام به طور پیش‌فرض ALPN h2 پیشنهاد می‌کند. اگر سرور آن را برگزیند با چیزی حرف می‌زنید که HTTP/2 انتظار دارد. برای انتقال پراکسی alpn = "http/1.1" بگذارید، یا "none" تا افزونه حذف شود.
  • بعضی بایت‌ها عمداً تصادفی‌اند. مقدارهای GREASE، طول ECH، عدد تصادفی، شناسه‌ی نشست و سهم‌های کلید با هر ساخت عوض می‌شوند. JA3 مقدارهای GREASE و بایت کلید را نادیده می‌گیرد، پس JA3 هر نمایه پایدار است.
  • فقط TLS 1.3 و بدون HelloRetryRequest. دست‌دهی از پیام سفارشی با پیام روشن شکست می‌خورد اگر سرور گروه دیگری بخواهد یا فقط TLS 1.2 بداند.
  • پیام را آزاد کنید. Hello مالک بافرهایش است: وقتی تمام کردید آزادسازی سلام را صدا بزنید.

ساخت پیام و خواندن JA3

نمونه برای سه نمایه یک پیام با کنار گذاشتن سهم ترکیبی می‌سازد و رشته‌های JA3 را چاپ می‌کند. رشته‌ی JA3 نگارش، رمزها، شناسه‌ی افزونه‌ها، منحنی‌ها و قالب نقطه‌هاست، هرکدام به صورت عدد دهدهی و با حذف GREASE. ترتیب شناسه‌ی افزونه‌ها روشن‌ترین تفاوت میان نمایه‌هاست.

net-utls-guide-profiles.salam
// زبان: فارسی
واردسازی شبکه.اثر انگشت تی ال اس

تابع ریشه:
    سرچاپ "نمایه‌ها:"، اثر انگشت تی ال اس.نام نمایه‌ها()
    ناپایا o := اثر انگشت تی ال اس.گزینه‌ها {}
    o.نام کارساز = "www.example.com"
    o.کلید پسا کوانتومی = نادرست
    هر name در ["chrome"، "firefox"، "safari"]:
        ناپایا h := اثر انگشت تی ال اس.ساخت(اثر انگشت تی ال اس.نمایه(name)، o)
        سرچاپ name، "پیام آغازین بزرگ‌تر از ۴۰۰ بایت است:"، h.خام.طول بزرگتر 400
        سرچاپ اثر انگشت تی ال اس.جی ای سه(h.خام)
        اثر انگشت تی ال اس.آزادسازی سلام(h)
    پایان
    سرچاپ "نمایه‌ی ناشناخته معتبر است:"، اثر انگشت تی ال اس.نمایه("netscape").معتبر
پایان
خروجینمایه‌ها: chrome,edge,firefox,safari,ios,randomized chrome پیام آغازین بزرگ‌تر از ۴۰۰ بایت است: true 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-65037,29-23-24,0 firefox پیام آغازین بزرگ‌تر از ۴۰۰ بایت است: true 771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-34-51-43-13-45-28-65037,29-23-24-25-256-257,0 safari پیام آغازین بزرگ‌تر از ۴۰۰ بایت است: true 771,4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-61-60-53-47-49160-49170-10,0-23-65281-10-11-16-5-13-18-51-45-43-27-21,29-23-24-25,0 نمایه‌ی ناشناخته معتبر است: false

دست‌دهی از یک نمایه

پوشش کارخواه سوکت وصل‌شده، مشخصه، گزینه‌ها و یک tls.Config می‌گیرد. گواهی به شکل عادی بررسی می‌شود. نتیجه یک tls.Conn است، پس هر تابع tls و پشتیبانی TLS انتقال شبکه با آن کار می‌کند.

net-utls-guide-handshake.salam
// زبان: فارسی
واردسازی شبکه.تی سی پی
واردسازی شبکه.اثر انگشت تی ال اس
واردسازی تی ال اس

تابع ریشه:
    c := تی سی پی.شماره گیری("www.example.com"، 443)
    ناپایا o := اثر انگشت تی ال اس.گزینه‌ها {}
    o.نام کارساز = "www.example.com"
    o.پروتکل برنامه = "http/1.1"
    cfg := تی ال اس.پیکربندی جدید("www.example.com")
    ناپایا t := اثر انگشت تی ال اس.پوشش کارخواه(c.توصیفگر، اثر انگشت تی ال اس.نمایه("chrome")، o، cfg)
    اگر وارونه تی ال اس.اتصال معتبر(t):
        سرچاپ "شکست خورد:"، تی ال اس.خطای اتصال(t)
        برگشت 1
    پایان
    تی ال اس.نوشتن(t، "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n")
    سرچاپ تی ال اس.خواندن(t، 200)
    تی ال اس.بستن اتصال(t)
پایان

محدودیت‌ها

  • نمایه‌ها تقریب‌اند، نه ضبط. با مرورگر زنده مقایسه نشدند و کارخواه‌های واقعی از انتشاری به انتشار دیگر تغییر می‌کنند.
  • ML-KEM ندارد. سهم ترکیبی جانگهدار است (بالا را ببینید) و سروری که آن را برگزیند دست‌دهی را می‌شکند.
  • از سرگیری نشست و افزونه‌ی کلید پیش‌اشتراکی ندارد. پیام همیشه اتصال اول است.
  • افزونه‌ی ECH فقط GREASE است. شکل یکی واقعی را دارد و چیزی در آن رمز نشده.
  • لایه‌ی رکورد و دست‌دهی از کتابخانه است. رکورد اول مثل مرورگرها نگارش قدیمی 0x0301 دارد، اما طول‌ها و زمان‌بندی آنچه پس از پیام می‌آید مال بسته‌ی tls است، نه مرورگر.
  • فقط TLS 1.3. سروری که با TLS 1.2 جواب بدهد دست‌دهی را شکست می‌دهد.

گونه‌ها

بایت‌ها Bytes

ساختار بایت‌ها
یک بافر بایت که این بسته می‌گیرد.
نامانگلیسیگونهتوضیح
نشانگرpتهی*ابتدای بایت‌ها.
طولlenصحیح = 0چند بایت استفاده شده.
ظرفیتcapصحیح = 0چند بایت گرفته شده.

مشخصات Spec

ساختار مشخصات
توصیف ClientHello یک خانواده‌ی کارخواه.
نامانگلیسیگونهتوضیح
نامnameرشته = ""نام نمایه.
رمزهاciphersرشته = ""رمزها به ترتیب، به صورت عدد شانزدهی. g0 مقدار GREASE است.
افزونه‌هاextensionsرشته = ""شناسه‌ی افزونه‌ها به ترتیب روی سیم. g1 و g4 افزونه‌های GREASE هستند و pad جای پرکننده.
گروه‌هاgroupsرشته = ""گروه‌های پشتیبانی‌شده به ترتیب.
سهم کلیدهاkey_sharesرشته = ""سهم‌های کلیدی که فرستاده می‌شود: 001d X25519، 0017 secp256r1، 11ec X25519MLKEM768، g2 GREASE.
الگوریتم‌های امضاsigalgsرشته = ""الگوریتم‌های امضا به ترتیب.
نسخه‌هاversionsرشته = ""نگارش‌های پشتیبانی‌شده به ترتیب، g3 برای مقدار GREASE.
پروتکل برنامهalpnرشته = ""نام پروتکل‌های ALPN، جداشده با ویرگول.
فشرده‌سازی گواهیcert_compressرشته = ""الگوریتم‌های فشرده‌سازی گواهی، به صورت عدد شانزدهی.
اعتبارنامه تفویضیdelegatedرشته = ""الگوریتم‌های امضای اعتبارنامه‌ی تفویضی، به صورت عدد شانزدهی.
حد اندازه رکوردrecord_size_limitصحیح = 0مقدار افزونه‌ی حد اندازه‌ی رکورد، ۰ وقتی نمایه ندارد.
اندازه پرکنندهpad_toصحیح = 0پیام را وقتی میان ۲۵۶ بایت و این اندازه است تا این اندازه پر کن. ۰ پر کردن را خاموش می‌کند.
تصادفی سازیrandomizeمنطقی = نادرستافزونه‌ها و رمزها را در هر ساخت بر بزن.
معتبرokمنطقی = نادرستبرای نام نمایه‌ی ناشناخته نادرست است.

گزینه‌ها Options

ساختار گزینه‌ها
آنچه میان دو پیام از یک نمایه عوض می‌شود.
نامانگلیسیگونهتوضیح
نام کارسازserver_nameرشته = ""نام سرور، خالی یعنی هیچ.
پروتکل برنامهalpnرشته = ""فهرست ALPN را جایگزین می‌کند، جداشده با ویرگول. none افزونه را حذف می‌کند. خالی فهرست نمایه را نگه می‌دارد.
بر زدن افزونه‌هاshuffleمنطقی = نادرستترتیب افزونه‌ها را بر می‌زند، با نگه داشتن افزونه‌های GREASE در دو سر و پرکننده در آخر.
کلید پسا کوانتومیpqمنطقی = درستگروه ترکیبی پساکوانتومی و سهم کلیدش را بگنجان. نادرست آن‌ها را کنار می‌گذارد.
گریس اکوechمنطقی = درستافزونه‌ی GREASE برای ECH را اگر نمایه دارد بگنجان.
پرکنندهpaddingمنطقی = درستافزونه‌ی پرکننده را اگر نمایه دارد اضافه کن.

سلام کارخواه Hello

ساختار سلام کارخواه
یک ClientHello ساخته‌شده و جفت کلیدی که با آن می‌آید.
نامانگلیسیگونهتوضیح
خامrawبایت‌هاپیام دست‌دهی، از بایت نوع به بعد، بدون سرآیند رکورد.
کلید خصوصیprivبایت‌هاکلید خصوصی X25519 سهم کلید.
کلید عمومیpub_keyبایت‌هاکلید عمومی X25519 سهم کلید.
بذر پسا کوانتومیpq_seedبایت‌هابذر ۶۴ بایتی ML-KEM پشت کلید مشترک X25519MLKEM768. اگر پیام آغازین چنین کلیدی نداشته باشد تهی است.
کلید عمومی پسا کوانتومیpq_ekبایت‌هاکلید کپسوله‌سازی ML-KEM-768 که در کلید مشترک رفته است.
معتبرokمنطقی = نادرستپیام ساخته شد.
خطاerrرشته = ""چرا ساخته نشد.

تابع‌ها

جی ای سه JA3

تابع جی ای سه(b: بایت‌ها): رشته
متن JA3 یک پیام: نگارش، رمزها، افزونه‌ها، گروه‌ها و قالب نقطه‌ها، بدون GREASE.

آزادسازی بایت‌ها FreeBytes

تابع آزادسازی بایت‌ها(b &: بایت‌ها)
یک بافر را آزاد می‌کند.

آزادسازی سلام FreeHello

تابع آزادسازی سلام(h &: سلام کارخواه)
بافرهای یک پیام را آزاد می‌کند.

به شانزدهی ToHex

تابع به شانزدهی(b: بایت‌ها): رشته
بایت‌ها به صورت هگز با حروف کوچک.

جایگاه شناسه نشست SessionIdOffset

تابع جایگاه شناسه نشست(): صحیح
جایی که شناسه‌ی نشست ۳۲ بایتی درون raw شروع می‌شود.

تنظیم شناسه نشست SetSessionId

تابع تنظیم شناسه نشست(h &: سلام کارخواه، sid: تهی*)
شناسه‌ی نشست ۳۲ بایتی یک پیام ساخته‌شده را بازنویسی می‌کند.

ساخت Build

تابع ساخت(sp: مشخصات، o: گزینه‌ها): سلام کارخواه
از یک مشخصه و گزینه‌ها پیام می‌سازد، با عدد تصادفی، جفت کلید و شناسه‌ی نشست تازه.

نمایه Profile

تابع نمایه(name: رشته): مشخصات
مشخصه‌ی یک نمایه‌ی نام‌دار. ok را بررسی کنید.

نام نمایه‌ها ProfileNames

تابع نام نمایه‌ها(): رشته
نام نمایه‌هایی که با کارسازهای امروزی کار می‌کنند، جداشده با ویرگول. randomized روی chrome ساخته شده و سهم کلید ترکیبی هم دارد.

نام نمایه‌های قدیمی LegacyProfileNames

تابع نام نمایه‌های قدیمی(): رشته
نام نمایه‌های قدیمی، جداشده با ویرگول (chrome_120 که سهم کلید ترکیبی ندارد و ایکس‌ری ۲۶.۹.۹ و بعدتر آن را نمی‌پذیرند).

رکورد Record

تابع رکورد(h: سلام کارخواه): بایت‌ها
پیام پیچیده در یک رکورد TLS (نوع ۲۲، نگارش 0x0301).

بذر پسا کوانتومی سلام PqSeed

تابع بذر پسا کوانتومی سلام(h: سلام کارخواه): تهی*
بذر ML-KEM یک پیام آغازین، یا تهی اگر نداشته باشد. tls.WrapClientRawPq آن را می‌گیرد.

پوشش کارخواه WrapClient

تابع پوشش کارخواه(fd: صحیح۶۴، sp: مشخصات، o: گزینه‌ها، cfg: تی ال اس.پیکربندی): تی ال اس.اتصال
پیام می‌سازد و با آن دست‌دهی کارخواه TLS 1.3 را روی سوکت وصل‌شده اجرا می‌کند.