codex / src /core /openai /sentinel.py
webhook's picture
Upload 137 files
89419ea verified
Raw History Blame Contribute Delete
3.6 kB
"""Helpers for OpenAI Sentinel proof-of-work tokens."""
from __future__ import annotations
import base64
import hashlib
import json
import random
import time
import uuid
from datetime import datetime, timedelta, timezone
from typing import Sequence
DEFAULT_SENTINEL_DIFF = "0fffff"
DEFAULT_MAX_ITERATIONS = 500_000
_SCREEN_SIGNATURES = ("1920x1080", "2560x1440", "1366x768", "1536x864", "1440x900")
_LANGUAGE_SIGNATURE = "en-US,en"
_NAVIGATOR_KEYS = (
"vendorSub", "productSub", "vendor", "maxTouchPoints",
"scheduling", "userActivation", "doNotTrack", "geolocation",
"connection", "plugins", "mimeTypes", "pdfViewerEnabled",
"webkitTemporaryStorage", "webkitPersistentStorage",
"hardwareConcurrency", "cookieEnabled", "credentials",
"mediaDevices", "permissions", "locks", "ink",
)
_WINDOW_KEYS = (
"Object", "Function", "Array", "Number", "parseFloat",
"undefined", "window", "document", "navigator",
)
class SentinelPOWError(RuntimeError):
"""Raised when a Sentinel proof-of-work token cannot be solved."""
def _format_browser_time() -> str:
"""Match the browser-style timestamp used by public Sentinel solvers."""
browser_now = datetime.now(timezone.utc)
return browser_now.strftime("%a %b %d %Y %H:%M:%S") + " GMT+0000 (Coordinated Universal Time)"
def build_sentinel_config(user_agent: str) -> list:
"""Build a browser-like fingerprint payload for the Sentinel PoW solver."""
perf_ms = time.perf_counter() * 1000
epoch_ms = (time.time() * 1000) - perf_ms
return [
random.choice(_SCREEN_SIGNATURES),
_format_browser_time(),
4294705152,
0,
user_agent,
"",
"",
"en-US",
_LANGUAGE_SIGNATURE,
0,
random.choice(_NAVIGATOR_KEYS),
"location",
random.choice(_WINDOW_KEYS),
perf_ms,
str(uuid.uuid4()),
"",
8,
epoch_ms,
]
def _encode_pow_payload(config: Sequence[object], nonce: int) -> bytes:
prefix = (json.dumps(config[:3], separators=(",", ":"), ensure_ascii=False)[:-1] + ",").encode("utf-8")
middle = (
"," + json.dumps(config[4:9], separators=(",", ":"), ensure_ascii=False)[1:-1] + ","
).encode("utf-8")
suffix = ("," + json.dumps(config[10:], separators=(",", ":"), ensure_ascii=False)[1:]).encode("utf-8")
body = prefix + str(nonce).encode("ascii") + middle + str(nonce >> 1).encode("ascii") + suffix
return base64.b64encode(body)
def solve_sentinel_pow(
seed: str,
difficulty: str,
config: Sequence[object],
max_iterations: int = DEFAULT_MAX_ITERATIONS,
) -> str:
"""Solve the Sentinel PoW challenge and return the base64 payload."""
seed_bytes = seed.encode("utf-8")
target = bytes.fromhex(difficulty)
prefix_length = len(target)
for nonce in range(max_iterations):
encoded = _encode_pow_payload(config, nonce)
digest = hashlib.sha3_512(seed_bytes + encoded).digest()
if digest[:prefix_length] <= target:
return encoded.decode("ascii")
raise SentinelPOWError(f"failed to solve sentinel pow after {max_iterations} attempts")
def build_sentinel_pow_token(
user_agent: str,
difficulty: str = DEFAULT_SENTINEL_DIFF,
max_iterations: int = DEFAULT_MAX_ITERATIONS,
) -> str:
"""Build the `p` token required by the Sentinel request endpoint."""
config = build_sentinel_config(user_agent)
seed = format(random.random())
solution = solve_sentinel_pow(seed, difficulty, config, max_iterations=max_iterations)
return f"gAAAAAC{solution}"