| package prooftoken |
|
|
| import ( |
| "crypto/rand" |
| "encoding/base64" |
| "encoding/json" |
| "fmt" |
| mathrand "math/rand" |
| "strconv" |
| "time" |
|
|
| "aurora/internal/browserfp" |
| "aurora/internal/fingerprint" |
| "aurora/util" |
| ) |
|
|
| |
| type mathRand = mathrand.Rand |
|
|
| func mathRandNew(seed int64) *mathRand { |
| return mathrand.New(mathrand.NewSource(seed)) |
| } |
|
|
| const ( |
| |
| PrefixRequirements = "gAAAAAC" |
| |
| PrefixProof = "gAAAAAB" |
| |
| Suffix = "~S" |
| |
| |
| |
| ErrorPrefix = "wQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D" |
| |
| |
| DefaultErrorPayload = "ImUi" |
| ) |
|
|
| |
| const DefaultFlow = "chatgpt" |
|
|
| |
| const fingerprintSize = 25 |
|
|
| |
| var windowKeys = []string{ |
| "requestIdleCallback", "webkitRequestAnimationFrame", "onfocus", "onblur", |
| } |
|
|
| |
| var reactLetters = []rune("abcdefghijklmnopqrstuvwxyz0123456789") |
|
|
| |
| type Config struct { |
| DeviceID string |
| UserAgent string |
| Language string |
| Languages string |
| |
| |
| Timezone string |
| ScreenWidth int |
| ScreenHeight int |
| HardwareConcurrency int |
| SentinelSV string |
| BuildID string |
| |
| FixedRandom *float64 |
| } |
|
|
| |
| |
| func NewConfig(userAgent string) *Config { |
| if userAgent == "" { |
| userAgent = util.FixedUserAgent |
| } |
| fp := browserfp.Get() |
| return &Config{ |
| DeviceID: randomUUID(), |
| UserAgent: userAgent, |
| Language: fp.Language, |
| Languages: browserfp.LanguageJoin(fp.Language), |
| Timezone: "America/Los_Angeles", |
| ScreenWidth: fp.ScreenWidth, |
| ScreenHeight: fp.ScreenHeight, |
| HardwareConcurrency: fp.HardwareConcurrency, |
| SentinelSV: "20260423af3c", |
| BuildID: fp.BuildID, |
| } |
| } |
|
|
| |
| func randomUUID() string { |
| var b [16]byte |
| _, _ = rand.Read(b[:]) |
| b[6] = (b[6] & 0x0f) | 0x40 |
| b[8] = (b[8] & 0x3f) | 0x80 |
| return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", |
| b[0:4], b[4:6], b[6:8], b[8:10], b[10:16]) |
| } |
|
|
| |
| func EncodeConfig(config []any) string { |
| b, err := json.Marshal(config) |
| if err != nil { |
| return "" |
| } |
| return base64.StdEncoding.EncodeToString(b) |
| } |
|
|
| |
| |
| |
| func EncodeString(s string) string { |
| b, err := json.Marshal(s) |
| if err != nil { |
| return "" |
| } |
| return base64.StdEncoding.EncodeToString(b) |
| } |
|
|
| |
| |
| func FNV1aHash(text string) string { |
| const ( |
| fnvOffset = 2166136261 |
| fnvPrime = 16777619 |
| ) |
| h := uint32(fnvOffset) |
| for _, ch := range text { |
| h ^= uint32(ch) |
| h = imul32(h, fnvPrime) |
| } |
| h ^= h >> 16 |
| h = imul32(h, 2246822507) |
| h ^= h >> 13 |
| h = imul32(h, 3266489909) |
| h ^= h >> 16 |
| return fmt.Sprintf("%08x", h) |
| } |
|
|
| |
| func imul32(a, b uint32) uint32 { |
| return (a * b) & 0xFFFFFFFF |
| } |
|
|
| |
| func (c *Config) rngFloat(rng *mathRand) float64 { |
| if c.FixedRandom != nil { |
| return *c.FixedRandom |
| } |
| return rng.Float64() |
| } |
|
|
| |
| |
| func (c *Config) envFlags() [4]int { |
| return [4]int{0, 0, 0, 0} |
| } |
|
|
| |
| |
| |
| |
| func (c *Config) buildConfig(rng *mathRand, attempt *int, elapsedMs *int64) []any { |
| |
| opts := fingerprint.Options{ |
| UserAgent: c.UserAgent, |
| Platform: "Win32", |
| ScreenWidth: c.ScreenWidth, |
| ScreenHeight: c.ScreenHeight, |
| HardwareConcurrency: c.HardwareConcurrency, |
| JSHeapSizeLimit: 4294967296, |
| BuildID: c.BuildID, |
| Timezone: c.Timezone, |
| Rand: rng, |
| } |
| |
| if c.Languages != "" { |
| opts.Languages = splitLangList(c.Languages) |
| } else { |
| opts.Languages = []string{c.Language, "en"} |
| } |
|
|
| config := fingerprint.Build25(opts) |
|
|
| |
| if attempt != nil { |
| config[3] = *attempt |
| } else { |
| |
| |
| |
| config[3] = 1 |
| } |
| if elapsedMs != nil { |
| config[9] = *elapsedMs |
| } else { |
| |
| |
| |
| config[9] = c.rngFloat(rng) |
| } |
| |
| |
| if c.DeviceID != "" { |
| config[14] = c.DeviceID |
| } |
| return config |
| } |
|
|
| |
| func splitLangList(s string) []string { |
| out := []string{} |
| cur := "" |
| for _, r := range s { |
| if r == ',' { |
| if cur != "" { |
| out = append(out, cur) |
| cur = "" |
| } |
| continue |
| } |
| cur += string(r) |
| } |
| if cur != "" { |
| out = append(out, cur) |
| } |
| return out |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func (c *Config) GenerateRequirementsToken() string { |
| rng := mathRandNew(time.Now().UnixNano()) |
| |
| |
| config := c.buildConfig(rng, nil, nil) |
| config[3] = 1 |
| encoded := EncodeConfig(config) |
| return PrefixRequirements + encoded + Suffix |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| func (c *Config) SolveProofOfWork(seed, difficulty string) string { |
| if seed == "" || difficulty == "" { |
| return PrefixProof + Suffix |
| } |
| startTime := time.Now() |
| rng := mathRandNew(time.Now().UnixNano()) |
| diffLen := len(difficulty) |
| const maxIter = 500_000 |
|
|
| for i := 0; i < maxIter; i++ { |
| nonce := i |
| elapsed := time.Since(startTime).Milliseconds() |
| config := c.buildConfig(rng, &nonce, &elapsed) |
| encoded := EncodeConfig(config) |
| hashInput := seed + encoded |
| hashResult := FNV1aHash(hashInput) |
| if hashResult[:diffLen] <= difficulty { |
| return PrefixProof + encoded + Suffix |
| } |
| } |
| |
| return PrefixProof + ErrorPrefix + DefaultErrorPayload + Suffix |
| } |
|
|
| |
| |
| func BuildFailToken(errMessage string) string { |
| if errMessage == "" { |
| errMessage = "e" |
| } |
| return PrefixProof + ErrorPrefix + EncodeString(errMessage) + Suffix |
| } |
|
|
| |
| func String(v float64) string { |
| return strconv.FormatFloat(v, 'f', -1, 64) |
| } |
|
|
| |
|
|
| |
| func (c *Config) RequirementsToken() string { |
| return c.GenerateRequirementsToken() |
| } |
|
|
| |
| func SolveProofToken(seed, difficulty, userAgent string) string { |
| c := NewConfig(userAgent) |
| return c.SolveProofOfWork(seed, difficulty) |
| } |
|
|
| |
| func BuildSentinelRequestBody(p, deviceID, flow string) string { |
| if flow == "" { |
| flow = DefaultFlow |
| } |
| body := map[string]string{"p": p, "id": deviceID, "flow": flow} |
| b, _ := json.Marshal(body) |
| return string(b) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| type SentinelTokenHeader struct { |
| P string `json:"p"` |
| T string `json:"t"` |
| C string `json:"c"` |
| ID string `json:"id"` |
| Flow string `json:"flow"` |
| } |
|
|
| |
| func BuildSentinelTokenHeader(p, turnstileToken, sentinelToken, deviceID, flow string) string { |
| if flow == "" { |
| flow = DefaultFlow |
| } |
| h := SentinelTokenHeader{P: p, T: turnstileToken, C: sentinelToken, ID: deviceID, Flow: flow} |
| b, _ := json.Marshal(h) |
| return string(b) |
| } |
|
|