Skip to content

Commit 104500c

Browse files
committed
revert: restore original turnstile and prooftoken implementations
1 parent d2d1e81 commit 104500c

3 files changed

Lines changed: 645 additions & 200 deletions

File tree

internal/prooftoken/prooftoken.go

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
package prooftoken
2+
3+
import (
4+
"bytes"
5+
"encoding/base64"
6+
"encoding/hex"
7+
"encoding/json"
8+
"fmt"
9+
"math/rand"
10+
"regexp"
11+
"strings"
12+
"time"
13+
14+
"github.com/google/uuid"
15+
"golang.org/x/crypto/sha3"
16+
)
17+
18+
const (
19+
defaultPowScript = "https://chatgpt.com/backend-api/sentinel/sdk.js"
20+
maxAttempts = 500000
21+
)
22+
23+
var (
24+
cores = []int{8, 16, 24, 32}
25+
screenValues = []int{3000, 4000, 5000}
26+
documentKeys = []string{
27+
"_reactListeningo743lnnpvdg",
28+
"location",
29+
}
30+
navigatorKeys = []string{
31+
"registerProtocolHandler−function registerProtocolHandler() { [native code] }",
32+
"storage−[object StorageManager]",
33+
"locks−[object LockManager]",
34+
"appCodeName−Mozilla",
35+
"permissions−[object Permissions]",
36+
"share−function share() { [native code] }",
37+
"webdriver−false",
38+
"managed−[object NavigatorManagedData]",
39+
"canShare−function canShare() { [native code] }",
40+
"vendor−Google Inc.",
41+
"mediaDevices−[object MediaDevices]",
42+
"vibrate−function vibrate() { [native code] }",
43+
"storageBuckets−[object StorageBucketManager]",
44+
"mediaCapabilities−[object MediaCapabilities]",
45+
"cookieEnabled−true",
46+
"virtualKeyboard−[object VirtualKeyboard]",
47+
"product−Gecko",
48+
"presentation−[object Presentation]",
49+
"onLine−true",
50+
"mimeTypes−[object MimeTypeArray]",
51+
"credentials−[object CredentialsContainer]",
52+
"serviceWorker−[object ServiceWorkerContainer]",
53+
"keyboard−[object Keyboard]",
54+
"gpu−[object GPU]",
55+
"doNotTrack",
56+
"serial−[object Serial]",
57+
"pdfViewerEnabled−true",
58+
"language−zh-CN",
59+
"geolocation−[object Geolocation]",
60+
"userAgentData−[object NavigatorUAData]",
61+
"getUserMedia−function getUserMedia() { [native code] }",
62+
"sendBeacon−function sendBeacon() { [native code] }",
63+
"hardwareConcurrency−32",
64+
"windowControlsOverlay−[object WindowControlsOverlay]",
65+
}
66+
windowKeys = []string{
67+
"0",
68+
"window",
69+
"self",
70+
"document",
71+
"name",
72+
"location",
73+
"customElements",
74+
"history",
75+
"navigation",
76+
"innerWidth",
77+
"innerHeight",
78+
"scrollX",
79+
"scrollY",
80+
"visualViewport",
81+
"screenX",
82+
"screenY",
83+
"outerWidth",
84+
"outerHeight",
85+
"devicePixelRatio",
86+
"screen",
87+
"chrome",
88+
"navigator",
89+
"onresize",
90+
"performance",
91+
"crypto",
92+
"indexedDB",
93+
"sessionStorage",
94+
"localStorage",
95+
"scheduler",
96+
"alert",
97+
"atob",
98+
"btoa",
99+
"fetch",
100+
"matchMedia",
101+
"postMessage",
102+
"queueMicrotask",
103+
"requestAnimationFrame",
104+
"setInterval",
105+
"setTimeout",
106+
"caches",
107+
"__NEXT_DATA__",
108+
"__BUILD_MANIFEST",
109+
"__NEXT_PRELOADREADY",
110+
}
111+
scriptSrcRE = regexp.MustCompile(`<script\b[^>]*\bsrc=["']([^"']+)["']`)
112+
dataBuildRE = regexp.MustCompile(`(?:c/[^/]*/_|<html[^>]*data-build=["']([^"']*)["'])`)
113+
)
114+
115+
type ProofWork struct {
116+
Difficulty string `json:"difficulty,omitempty"`
117+
Required bool `json:"required"`
118+
Seed string `json:"seed,omitempty"`
119+
Ospt string `json:"-"`
120+
}
121+
122+
type Resources struct {
123+
ScriptSources []string
124+
DataBuild string
125+
}
126+
127+
func ParseResources(html string) Resources {
128+
resources := Resources{}
129+
for _, match := range scriptSrcRE.FindAllStringSubmatch(html, -1) {
130+
resources.ScriptSources = append(resources.ScriptSources, match[1])
131+
if resources.DataBuild == "" {
132+
if build := regexp.MustCompile(`c/[^/]*/_`).FindString(match[1]); build != "" {
133+
resources.DataBuild = build
134+
}
135+
}
136+
}
137+
if len(resources.ScriptSources) == 0 {
138+
resources.ScriptSources = []string{defaultPowScript}
139+
}
140+
if resources.DataBuild == "" {
141+
for _, match := range dataBuildRE.FindAllStringSubmatch(html, -1) {
142+
if len(match) > 1 && match[1] != "" {
143+
resources.DataBuild = match[1]
144+
break
145+
}
146+
if match[0] != "" && strings.HasPrefix(match[0], "c/") {
147+
resources.DataBuild = match[0]
148+
break
149+
}
150+
}
151+
}
152+
return resources
153+
}
154+
155+
func CalcProofToken(seed string, difficulty string, userAgent string, resources ...Resources) string {
156+
answer, ok := generate(seed, difficulty, buildConfig(userAgent, firstResource(resources)))
157+
if !ok {
158+
return ""
159+
}
160+
return "gAAAAAB" + answer
161+
}
162+
163+
func LegacyRequirementsToken(userAgent string, resources ...Resources) string {
164+
seed := fmt.Sprintf("%v", rand.New(rand.NewSource(time.Now().UnixNano())).Float64())
165+
answer, _ := generate(seed, "0fffff", buildConfig(userAgent, firstResource(resources)))
166+
return "gAAAAAC" + answer
167+
}
168+
169+
func firstResource(resources []Resources) Resources {
170+
if len(resources) > 0 {
171+
return resources[0]
172+
}
173+
return Resources{ScriptSources: []string{defaultPowScript}}
174+
}
175+
176+
func buildConfig(userAgent string, resources Resources) []interface{} {
177+
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
178+
scriptSources := resources.ScriptSources
179+
if len(scriptSources) == 0 {
180+
scriptSources = []string{defaultPowScript}
181+
}
182+
now := time.Now()
183+
perfMs := float64(now.UnixNano()%int64(time.Second)) / float64(time.Millisecond)
184+
return []interface{}{
185+
screenValues[rng.Intn(len(screenValues))],
186+
legacyParseTime(),
187+
int64(4294705152),
188+
0,
189+
userAgent,
190+
scriptSources[rng.Intn(len(scriptSources))],
191+
resources.DataBuild,
192+
"en-US",
193+
"en-US,es-US,en,es",
194+
0,
195+
navigatorKeys[rng.Intn(len(navigatorKeys))],
196+
documentKeys[rng.Intn(len(documentKeys))],
197+
windowKeys[rng.Intn(len(windowKeys))],
198+
perfMs,
199+
uuid.NewString(),
200+
"",
201+
cores[rng.Intn(len(cores))],
202+
float64(now.UnixMilli()) - perfMs,
203+
}
204+
}
205+
206+
func legacyParseTime() string {
207+
loc := time.FixedZone("Eastern Standard Time", -5*60*60)
208+
return time.Now().In(loc).Format("Mon Jan 02 2006 15:04:05") + " GMT-0500 (Eastern Standard Time)"
209+
}
210+
211+
func generate(seed string, difficulty string, config []interface{}) (string, bool) {
212+
target, err := hex.DecodeString(difficulty)
213+
if err != nil || len(target) == 0 {
214+
return "", false
215+
}
216+
diffLen := len(difficulty) / 2
217+
seedBytes := []byte(seed)
218+
static1 := mustJSONPrefix(config[:3])
219+
static2 := mustJSONMiddle(config[4:9])
220+
static3 := mustJSONSuffix(config[10:])
221+
hasher := sha3.New512()
222+
for i := 0; i < maxAttempts; i++ {
223+
finalJSON := bytes.NewBuffer(make([]byte, 0, 512))
224+
finalJSON.Write(static1)
225+
finalJSON.WriteString(fmt.Sprintf("%d", i))
226+
finalJSON.Write(static2)
227+
finalJSON.WriteString(fmt.Sprintf("%d", i>>1))
228+
finalJSON.Write(static3)
229+
encoded := []byte(base64.StdEncoding.EncodeToString(finalJSON.Bytes()))
230+
hasher.Write(seedBytes)
231+
hasher.Write(encoded)
232+
digest := hasher.Sum(nil)
233+
hasher.Reset()
234+
if bytes.Compare(digest[:diffLen], target) <= 0 {
235+
return string(encoded), true
236+
}
237+
}
238+
fallback := "wQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D" + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%q", seed)))
239+
return fallback, false
240+
}
241+
242+
func mustJSONPrefix(values []interface{}) []byte {
243+
b, _ := json.Marshal(values)
244+
return append(b[:len(b)-1], ',')
245+
}
246+
247+
func mustJSONMiddle(values []interface{}) []byte {
248+
b, _ := json.Marshal(values)
249+
return append(append([]byte{','}, b[1:len(b)-1]...), ',')
250+
}
251+
252+
func mustJSONSuffix(values []interface{}) []byte {
253+
b, _ := json.Marshal(values)
254+
return append([]byte{','}, b[1:]...)
255+
}
256+
257+
// Compatibility wrappers for local API
258+
259+
func RequirementsToken(userAgent string, scriptSources []string, deviceID string) string {
260+
resources := Resources{ScriptSources: scriptSources}
261+
return LegacyRequirementsToken(userAgent, resources)
262+
}
263+
264+
func SolveProofToken(seed, difficulty, userAgent string, scriptSources []string, deviceID string) string {
265+
resources := Resources{ScriptSources: scriptSources}
266+
return CalcProofToken(seed, difficulty, userAgent, resources)
267+
}

0 commit comments

Comments
 (0)