dialer.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // Package tlsfingerprint provides TLS fingerprint simulation for HTTP clients.
  2. // It uses the utls library to create TLS connections that mimic Node.js/Claude Code clients.
  3. package tlsfingerprint
  4. import (
  5. "context"
  6. "crypto/x509"
  7. "fmt"
  8. "log/slog"
  9. "net"
  10. "net/url"
  11. utls "github.com/refraction-networking/utls"
  12. )
  13. // Profile contains TLS fingerprint configuration.
  14. // All slice fields use built-in defaults when empty.
  15. type Profile struct {
  16. Name string // Profile name for identification
  17. CipherSuites []uint16
  18. Curves []uint16
  19. PointFormats []uint16
  20. EnableGREASE bool
  21. SignatureAlgorithms []uint16 // Empty uses defaultSignatureAlgorithms
  22. ALPNProtocols []string // Empty uses ["http/1.1"]
  23. SupportedVersions []uint16 // Empty uses [TLS1.3, TLS1.2]
  24. KeyShareGroups []uint16 // Empty uses [X25519]
  25. PSKModes []uint16 // Empty uses [psk_dhe_ke]
  26. Extensions []uint16 // Extension type IDs in order; empty uses default Node.js 24.x order
  27. }
  28. // Dialer creates TLS connections with custom fingerprints.
  29. type Dialer struct {
  30. profile *Profile
  31. baseDialer func(ctx context.Context, network, addr string) (net.Conn, error)
  32. }
  33. // HTTPProxyDialer creates TLS connections through HTTP/HTTPS proxies with custom fingerprints.
  34. // It handles the CONNECT tunnel establishment before performing TLS handshake.
  35. type HTTPProxyDialer struct {
  36. profile *Profile
  37. proxyURL *url.URL
  38. }
  39. // SOCKS5ProxyDialer creates TLS connections through SOCKS5 proxies with custom fingerprints.
  40. // It uses golang.org/x/net/proxy to establish the SOCKS5 tunnel.
  41. type SOCKS5ProxyDialer struct {
  42. profile *Profile
  43. proxyURL *url.URL
  44. }
  45. // Default TLS fingerprint values captured from Claude Code (Node.js 24.x)
  46. // Captured via tls-fingerprint-web capture server
  47. // JA3 Hash: 44f88fca027f27bab4bb08d4af15f23e
  48. // JA4: t13d1714h1_5b57614c22b0_7baf387fc6ff
  49. var (
  50. // defaultCipherSuites contains the 17 cipher suites from Node.js 24.x
  51. // Order is critical for JA3 fingerprint matching
  52. defaultCipherSuites = []uint16{
  53. // TLS 1.3 cipher suites
  54. 0x1301, // TLS_AES_128_GCM_SHA256
  55. 0x1302, // TLS_AES_256_GCM_SHA384
  56. 0x1303, // TLS_CHACHA20_POLY1305_SHA256
  57. // ECDHE + AES-GCM
  58. 0xc02b, // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  59. 0xc02f, // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  60. 0xc02c, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  61. 0xc030, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  62. // ECDHE + ChaCha20-Poly1305
  63. 0xcca9, // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
  64. 0xcca8, // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
  65. // ECDHE + AES-CBC-SHA (legacy fallback)
  66. 0xc009, // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
  67. 0xc013, // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
  68. 0xc00a, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
  69. 0xc014, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
  70. // RSA + AES-GCM (non-PFS)
  71. 0x009c, // TLS_RSA_WITH_AES_128_GCM_SHA256
  72. 0x009d, // TLS_RSA_WITH_AES_256_GCM_SHA384
  73. // RSA + AES-CBC-SHA (non-PFS, legacy)
  74. 0x002f, // TLS_RSA_WITH_AES_128_CBC_SHA
  75. 0x0035, // TLS_RSA_WITH_AES_256_CBC_SHA
  76. }
  77. // defaultCurves contains the 3 supported groups from Node.js 24.x
  78. defaultCurves = []utls.CurveID{
  79. utls.X25519, // 0x001d
  80. utls.CurveP256, // 0x0017 (secp256r1)
  81. utls.CurveP384, // 0x0018 (secp384r1)
  82. }
  83. // defaultPointFormats contains point formats from Node.js 24.x
  84. defaultPointFormats = []uint16{
  85. 0, // uncompressed
  86. }
  87. // defaultSignatureAlgorithms contains the 9 signature algorithms from Node.js 24.x
  88. defaultSignatureAlgorithms = []utls.SignatureScheme{
  89. 0x0403, // ecdsa_secp256r1_sha256
  90. 0x0804, // rsa_pss_rsae_sha256
  91. 0x0401, // rsa_pkcs1_sha256
  92. 0x0503, // ecdsa_secp384r1_sha384
  93. 0x0805, // rsa_pss_rsae_sha384
  94. 0x0501, // rsa_pkcs1_sha384
  95. 0x0806, // rsa_pss_rsae_sha512
  96. 0x0601, // rsa_pkcs1_sha512
  97. 0x0201, // rsa_pkcs1_sha1
  98. }
  99. )
  100. // NewDialer creates a new TLS fingerprint dialer.
  101. // baseDialer is used for TCP connection establishment (supports proxy scenarios).
  102. // If baseDialer is nil, direct TCP dial is used.
  103. func NewDialer(profile *Profile, baseDialer func(ctx context.Context, network, addr string) (net.Conn, error)) *Dialer {
  104. if baseDialer == nil {
  105. baseDialer = (&net.Dialer{}).DialContext
  106. }
  107. return &Dialer{profile: profile.Clone(), baseDialer: baseDialer}
  108. }
  109. // NewHTTPProxyDialer creates a new TLS fingerprint dialer that works through HTTP/HTTPS proxies.
  110. // It establishes a CONNECT tunnel before performing TLS handshake with custom fingerprint.
  111. func NewHTTPProxyDialer(profile *Profile, proxyURL *url.URL) *HTTPProxyDialer {
  112. return &HTTPProxyDialer{profile: profile.Clone(), proxyURL: proxyURL}
  113. }
  114. // NewSOCKS5ProxyDialer creates a new TLS fingerprint dialer that works through SOCKS5 proxies.
  115. // It establishes a SOCKS5 tunnel before performing TLS handshake with custom fingerprint.
  116. func NewSOCKS5ProxyDialer(profile *Profile, proxyURL *url.URL) *SOCKS5ProxyDialer {
  117. return &SOCKS5ProxyDialer{profile: profile.Clone(), proxyURL: proxyURL}
  118. }
  119. // DialTLSContext establishes a TLS connection through SOCKS5 proxy with the configured fingerprint.
  120. // Flow: SOCKS5 CONNECT to target -> TLS handshake with utls on the tunnel
  121. func (d *SOCKS5ProxyDialer) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error) {
  122. dial, err := configuredTLSDialer(d.profile, d.proxyURL, TransportOptions{})
  123. if err != nil {
  124. return nil, err
  125. }
  126. return dial(ctx, network, addr)
  127. }
  128. // DialTLSContext establishes a TLS connection through HTTP proxy with the configured fingerprint.
  129. // Flow: TCP connect to proxy -> CONNECT tunnel -> TLS handshake with utls
  130. func (d *HTTPProxyDialer) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error) {
  131. dial, err := configuredTLSDialer(d.profile, d.proxyURL, TransportOptions{})
  132. if err != nil {
  133. return nil, err
  134. }
  135. return dial(ctx, network, addr)
  136. }
  137. // DialTLSContext establishes a TLS connection with the configured fingerprint.
  138. // This method is designed to be used as http.Transport.DialTLSContext.
  139. func (d *Dialer) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error) {
  140. // Establish TCP connection using base dialer (supports proxy)
  141. slog.Debug("tls_fingerprint_dialing_tcp", "addr", addr)
  142. conn, err := d.baseDialer(ctx, network, addr)
  143. if err != nil {
  144. slog.Debug("tls_fingerprint_tcp_dial_failed", "error", err)
  145. return nil, err
  146. }
  147. slog.Debug("tls_fingerprint_tcp_connected", "addr", addr)
  148. // Perform TLS handshake with utls fingerprint
  149. return performTLSHandshake(ctx, conn, d.profile, addr)
  150. }
  151. // performTLSHandshake performs the uTLS handshake on an established connection.
  152. // It builds a ClientHello spec from the profile, applies it, and completes the handshake.
  153. // On failure, conn is closed and an error is returned.
  154. func performTLSHandshake(ctx context.Context, conn net.Conn, profile *Profile, addr string) (net.Conn, error) {
  155. return performTLSHandshakeWithRoots(ctx, conn, profile, addr, nil)
  156. }
  157. func performTLSHandshakeWithRoots(ctx context.Context, conn net.Conn, profile *Profile, addr string, roots *x509.CertPool) (net.Conn, error) {
  158. host, _, err := net.SplitHostPort(addr)
  159. if err != nil {
  160. host = addr
  161. }
  162. spec := buildClientHelloSpecFromProfile(profile)
  163. tlsConn := utls.UClient(conn, &utls.Config{ServerName: host, RootCAs: roots}, utls.HelloCustom)
  164. if err := tlsConn.ApplyPreset(spec); err != nil {
  165. _ = conn.Close()
  166. return nil, fmt.Errorf("apply TLS preset: %w", err)
  167. }
  168. if err := tlsConn.HandshakeContext(ctx); err != nil {
  169. _ = conn.Close()
  170. return nil, fmt.Errorf("TLS handshake failed: %w", err)
  171. }
  172. state := tlsConn.ConnectionState()
  173. slog.Debug("tls_fingerprint_handshake_success",
  174. "host", host,
  175. "version", state.Version,
  176. "cipher_suite", state.CipherSuite,
  177. "alpn", state.NegotiatedProtocol)
  178. return tlsConn, nil
  179. }
  180. // toUTLSCurves converts uint16 slice to utls.CurveID slice.
  181. func toUTLSCurves(curves []uint16) []utls.CurveID {
  182. result := make([]utls.CurveID, len(curves))
  183. for i, c := range curves {
  184. result[i] = utls.CurveID(c)
  185. }
  186. return result
  187. }
  188. // defaultExtensionOrder is the Node.js 24.x extension order.
  189. // Used when Profile.Extensions is empty.
  190. var defaultExtensionOrder = []uint16{
  191. 0, // server_name
  192. 65037, // encrypted_client_hello
  193. 23, // extended_master_secret
  194. 65281, // renegotiation_info
  195. 10, // supported_groups
  196. 11, // ec_point_formats
  197. 35, // session_ticket
  198. 16, // alpn
  199. 5, // status_request
  200. 13, // signature_algorithms
  201. 18, // signed_certificate_timestamp
  202. 51, // key_share
  203. 45, // psk_key_exchange_modes
  204. 43, // supported_versions
  205. }
  206. // isGREASEValue checks if a uint16 value matches the TLS GREASE pattern (0x?a?a).
  207. func isGREASEValue(v uint16) bool {
  208. return v&0x0f0f == 0x0a0a && v>>8 == v&0xff
  209. }
  210. // buildClientHelloSpecFromProfile constructs ClientHelloSpec from a Profile.
  211. // This is a standalone function that can be used by both Dialer and HTTPProxyDialer.
  212. func buildClientHelloSpecFromProfile(profile *Profile) *utls.ClientHelloSpec {
  213. // ApplyPreset fills handshake-specific GREASE/key-share state. Give each
  214. // concurrent handshake its own slices, even when using a pooled template.
  215. profile = profile.Clone()
  216. // Resolve effective values (profile overrides or built-in defaults)
  217. cipherSuites := defaultCipherSuites
  218. if profile != nil && len(profile.CipherSuites) > 0 {
  219. cipherSuites = profile.CipherSuites
  220. }
  221. curves := defaultCurves
  222. if profile != nil && len(profile.Curves) > 0 {
  223. curves = toUTLSCurves(profile.Curves)
  224. }
  225. pointFormats := defaultPointFormats
  226. if profile != nil && len(profile.PointFormats) > 0 {
  227. pointFormats = profile.PointFormats
  228. }
  229. signatureAlgorithms := defaultSignatureAlgorithms
  230. if profile != nil && len(profile.SignatureAlgorithms) > 0 {
  231. signatureAlgorithms = make([]utls.SignatureScheme, len(profile.SignatureAlgorithms))
  232. for i, s := range profile.SignatureAlgorithms {
  233. signatureAlgorithms[i] = utls.SignatureScheme(s)
  234. }
  235. }
  236. alpnProtocols := []string{"http/1.1"}
  237. if profile != nil && len(profile.ALPNProtocols) > 0 {
  238. alpnProtocols = profile.ALPNProtocols
  239. }
  240. supportedVersions := []uint16{utls.VersionTLS13, utls.VersionTLS12}
  241. if profile != nil && len(profile.SupportedVersions) > 0 {
  242. supportedVersions = profile.SupportedVersions
  243. }
  244. keyShareGroups := []utls.CurveID{utls.X25519}
  245. if profile != nil && len(profile.KeyShareGroups) > 0 {
  246. keyShareGroups = toUTLSCurves(profile.KeyShareGroups)
  247. }
  248. pskModes := []uint16{uint16(utls.PskModeDHE)}
  249. if profile != nil && len(profile.PSKModes) > 0 {
  250. pskModes = profile.PSKModes
  251. }
  252. enableGREASE := profile != nil && profile.EnableGREASE
  253. // Build key shares
  254. keyShares := make([]utls.KeyShare, len(keyShareGroups))
  255. for i, g := range keyShareGroups {
  256. keyShares[i] = utls.KeyShare{Group: g}
  257. }
  258. // Determine extension order
  259. extOrder := defaultExtensionOrder
  260. if profile != nil && len(profile.Extensions) > 0 {
  261. extOrder = profile.Extensions
  262. }
  263. // Build extensions list from the ordered IDs.
  264. // Parametric extensions (curves, sigalgs, etc.) are populated with resolved profile values.
  265. // Unknown IDs use GenericExtension (sends type ID with empty data).
  266. extensions := make([]utls.TLSExtension, 0, len(extOrder)+2)
  267. for _, id := range extOrder {
  268. if isGREASEValue(id) {
  269. extensions = append(extensions, &utls.UtlsGREASEExtension{})
  270. continue
  271. }
  272. switch id {
  273. case 0: // server_name
  274. extensions = append(extensions, &utls.SNIExtension{})
  275. case 5: // status_request (OCSP)
  276. extensions = append(extensions, &utls.StatusRequestExtension{})
  277. case 10: // supported_groups
  278. extensions = append(extensions, &utls.SupportedCurvesExtension{Curves: curves})
  279. case 11: // ec_point_formats
  280. extensions = append(extensions, &utls.SupportedPointsExtension{SupportedPoints: toUint8s(pointFormats)})
  281. case 13: // signature_algorithms
  282. extensions = append(extensions, &utls.SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: signatureAlgorithms})
  283. case 16: // alpn
  284. extensions = append(extensions, &utls.ALPNExtension{AlpnProtocols: alpnProtocols})
  285. case 18: // signed_certificate_timestamp
  286. extensions = append(extensions, &utls.SCTExtension{})
  287. case 23: // extended_master_secret
  288. extensions = append(extensions, &utls.ExtendedMasterSecretExtension{})
  289. case 35: // session_ticket
  290. extensions = append(extensions, &utls.SessionTicketExtension{})
  291. case 43: // supported_versions
  292. extensions = append(extensions, &utls.SupportedVersionsExtension{Versions: supportedVersions})
  293. case 45: // psk_key_exchange_modes
  294. extensions = append(extensions, &utls.PSKKeyExchangeModesExtension{Modes: toUint8s(pskModes)})
  295. case 50: // signature_algorithms_cert
  296. extensions = append(extensions, &utls.SignatureAlgorithmsCertExtension{SupportedSignatureAlgorithms: signatureAlgorithms})
  297. case 51: // key_share
  298. extensions = append(extensions, &utls.KeyShareExtension{KeyShares: keyShares})
  299. case 0xfe0d: // encrypted_client_hello (ECH, 65037)
  300. // Send GREASE ECH with random payload — mimics Node.js behavior when no real ECHConfig is available.
  301. // An empty GenericExtension causes "error decoding message" from servers that validate ECH format.
  302. extensions = append(extensions, &utls.GREASEEncryptedClientHelloExtension{})
  303. case 0xff01: // renegotiation_info
  304. extensions = append(extensions, &utls.RenegotiationInfoExtension{})
  305. default:
  306. // Unknown extension — send as GenericExtension (type ID + empty data).
  307. // This covers encrypt_then_mac(22) and any future extensions.
  308. extensions = append(extensions, &utls.GenericExtension{Id: id})
  309. }
  310. }
  311. // For default extension order with EnableGREASE, wrap with GREASE bookends
  312. if enableGREASE && (profile == nil || len(profile.Extensions) == 0) {
  313. extensions = append([]utls.TLSExtension{&utls.UtlsGREASEExtension{}}, extensions...)
  314. extensions = append(extensions, &utls.UtlsGREASEExtension{})
  315. }
  316. return &utls.ClientHelloSpec{
  317. CipherSuites: append([]uint16(nil), cipherSuites...),
  318. CompressionMethods: []uint8{0}, // null compression only (standard)
  319. Extensions: extensions,
  320. TLSVersMax: utls.VersionTLS13,
  321. TLSVersMin: utls.VersionTLS10,
  322. }
  323. }
  324. // toUint8s converts []uint16 to []uint8 (for utls fields that require []uint8).
  325. func toUint8s(vals []uint16) []uint8 {
  326. out := make([]uint8, len(vals))
  327. for i, v := range vals {
  328. out[i] = uint8(v)
  329. }
  330. return out
  331. }