File size: 3,813 Bytes
5ef6e9d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | /**
* geminigen.ai x-guard-id header generator
*
* Reverse-engineered from geminigen.ai/_nuxt/nBh81x6X.js
* Constants extracted from: window.__NUXT__.config.public.antibot
*
* Algorithm:
* stableId = 22-char alphanumeric string (persistent per server instance)
* timeBucket = Math.floor(Date.now() / 60000) β changes every 60 seconds
* domFp = "0".repeat(32) β server has no DOM
*
* hmacKey = SHA256(`${SECRET_KEY}:${stableId}`).slice(0, 32)
* signHash = SHA256(`${path}:${METHOD}:${hmacKey}:${timeBucket}:${SECRET_KEY}`)
*
* payload = [0x01, ...hexBytes(hmacKey), // 16 bytes
* ...uint32BE(timeBucket), // 4 bytes
* ...hexBytes(signHash), // 32 bytes
* ...hexBytes(domFp)] // 16 bytes β 69 bytes total
*
* x-guard-id = base64url(payload) (no padding)
*/
import { createHash, randomBytes } from "crypto";
const SECRET_KEY = "45NPBH$&";
const TIME_BUCKET_MS = 60_000;
const STABLE_ID_LEN = 22;
const DOM_FP = "0".repeat(32); // 32 hex zeros β 16 zero bytes
// ββ One stable-id per server instance ββββββββββββββββββββββββββββββββββββββββ
function makeStableId(): string {
const rand = randomBytes(16).toString("hex");
const hash = createHash("sha256").update(`server:${rand}`).digest("hex");
// Take alphanumeric chars from the hex (all are [0-9a-f] so fine for [A-Za-z0-9])
return hash.slice(0, STABLE_ID_LEN);
}
let _stableId: string = makeStableId();
/** Replace the stable-id (e.g., if you want to rotate it). */
export function rotateStableId(): void { _stableId = makeStableId(); }
// ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/** SHA-256 of a UTF-8 string β lowercase hex (64 chars). */
function sha256(str: string): string {
return createHash("sha256").update(str, "utf8").digest("hex");
}
/** Convert a hex string to a byte array (every 2 hex chars β 1 byte). */
function hexToBytes(hex: string): number[] {
const bytes: number[] = [];
for (let i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substring(i, 2 + i), 16));
}
return bytes;
}
/** Encode uint32 as 4 big-endian bytes. */
function uint32BE(n: number): number[] {
return [(n >>> 24) & 255, (n >>> 16) & 255, (n >>> 8) & 255, n & 255];
}
// ββ Public API βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/**
* Generate the `x-guard-id` header value for a given API path + HTTP method.
*
* @param path e.g. "/api/video-gen/grok-stream"
* @param method e.g. "post"
*/
export function generateGuardId(path: string, method: string): string {
const stableId = _stableId;
const timeBucket = Math.floor(Date.now() / TIME_BUCKET_MS);
// hmacKey: first 32 hex chars of SHA256("${SECRET_KEY}:${stableId}") β 16 bytes
const hmacKey = sha256(`${SECRET_KEY}:${stableId}`).slice(0, 32);
// signHash: full 64-hex SHA256 β 32 bytes
const signHash = sha256(`${path}:${method.toUpperCase()}:${hmacKey}:${timeBucket}:${SECRET_KEY}`);
const payload: number[] = [
1, // version byte (LK = 1)
...hexToBytes(hmacKey), // 16 bytes
...uint32BE(timeBucket), // 4 bytes
...hexToBytes(signHash), // 32 bytes
...hexToBytes(DOM_FP), // 16 bytes
];
return Buffer.from(payload).toString("base64url"); // Node β₯ 16, no trailing =
}
|