From a548b455b674a061854c8a609949c677bd7cab17 Mon Sep 17 00:00:00 2001 From: eva Date: Thu, 12 Feb 2026 17:53:52 +0000 Subject: [PATCH] fix: we're so back (again) will do a writeup on this later, for now you can read the code --- scripts/standalone-verify.ts | 86 +++++++++++++++++++------------- src/routes/+page.svelte | 2 +- src/routes/api/verify/+server.ts | 73 ++++++++++++++------------- 3 files changed, 90 insertions(+), 71 deletions(-) diff --git a/scripts/standalone-verify.ts b/scripts/standalone-verify.ts index 4ec3c89..9e5b966 100644 --- a/scripts/standalone-verify.ts +++ b/scripts/standalone-verify.ts @@ -7,6 +7,14 @@ const randomChoice = (arr: T[]): T => arr[Math.floor(Math.random() * arr.leng const BASE_URL = 'https://eu-west-1.faceassure.com'; +function generateId(id: string, sub: string, sessionId: string, delimiter = '|') { + let s = 0; + for (let a = '' + id + delimiter + sub + delimiter + sessionId, i = 0; i < a.length; i++) { + s = (s << 5) - s + a.charCodeAt(i); + s &= s; + } + return '' + s; +} function generateUserAgent() { const agents = [ 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1', @@ -103,7 +111,7 @@ function getRandomLocation() { return locations[Math.floor(Math.random() * locations.length)]; } -function generateMediaMetadata() { +function generateMediaMetadata(sub: string, sessionId: string) { const randomHex = () => Array.from({ length: 32 }, () => Math.floor(Math.random() * 16) @@ -143,7 +151,7 @@ function generateMediaMetadata() { ]; const spec = randomChoice(specs); - const deviceId = randomHex(); + const deviceId = generateId(randomHex(), sub, sessionId, '-'); return [ { @@ -305,7 +313,6 @@ async function verify(qrCodeUrlStr: string) { const userAgent = generateUserAgent(); const parsedUserAgent = parseUserAgent(userAgent); const location = getRandomLocation(); - const mediaMetadata = generateMediaMetadata(); const commonHeaders = { 'User-Agent': userAgent, @@ -394,8 +401,22 @@ async function verify(qrCodeUrlStr: string) { 'SLOWLY_DISTANCE_YOURSELF_FROM_THE_CAMERA', 'TOO_DARK' ]; + const noState = [ + 'VIDEO_PROCESSING', + 'STAY_STILL', + 'TURN_RIGHT', + 'ALIGN_YOUR_FACE_WITH_THE_CAMERA_UP', + 'ALIGN_YOUR_FACE_WITH_THE_CAMERA_DOWN', + 'SLIGHTLY_TILT_YOUR_HEAD_LEFT', + 'SLIGHTLY_TILT_YOUR_HEAD_RIGHT', + 'OPEN_YOUR_MOUTH' + ]; const timelines: Record = {}; states.forEach((state) => (timelines[state] = generateTimeline(completionTime))); + noState.forEach((state) => { + timelines[state] = []; + }); + return timelines; }; const baseAge = randomFloat(25.2, 26.0); @@ -433,6 +454,25 @@ async function verify(qrCodeUrlStr: string) { const { speeds: failedOpennessSpeeds, intervals: failedOpennessIntervals } = calculateSpeedAndIntervals(failedMeasurements, failedTimestamps); + const stateTimelines = generateStateTimelines(completionTime); + const stateCompletionTimes: Record = {}; + for (const [key, timeline] of Object.entries(stateTimelines)) { + if (timeline.length < 1) continue; + let completionTime = 0; + for (const time of timeline) { + completionTime += time.reduce((accumulator, currentValue) => accumulator + currentValue, 0); + } + + stateCompletionTimes[key] = completionTime; + } + const mediaMetadata = generateMediaMetadata(jwtPayload.sub, sessionData.session_id); + + const ageCheckSession = generateId( + mediaMetadata.find((m) => m.mediaKind === 'videoinput')!.mediaId, + jwtPayload.sub, + sessionData.session_id + ); + let payload = { request_type: 'complete_transaction', transaction_id: sessionData.transaction_id, @@ -468,7 +508,7 @@ async function verify(qrCodeUrlStr: string) { ver: 'v1.10.22', ufi: [] }, - ageCheckSession: '-' + randomInt(1000000000, 9999999999), + ageCheckSession: ageCheckSession, miscellaneous: { recordedOpennessStreak: recordedMeasurements, recordedSpeeds, @@ -547,8 +587,8 @@ async function verify(qrCodeUrlStr: string) { sdk_path: './face-capture-v1.10.22.js', model_version: 'v.2025.0', cropper_version: 'v.0.0.3', - start_time_stamp: currentTime - randomFloat(20, 60), - end_time_stamp: currentTime, + start_time_stamp: currentTime + Number(Math.random().toFixed(3)), + end_time_stamp: currentTime + completionTime + Number(Math.random().toFixed(3)), device_timezone: location.timezone, referring_page: `https://d3ogqhtsivkon3.cloudfront.net/?token=${token}&shi=false&from_qr_scan=true`, parent_page: `https://d3ogqhtsivkon3.cloudfront.net/dynamic_index.html?sl=${jwtPayload.jti}®ion=eu-central-1`, @@ -603,7 +643,7 @@ async function verify(qrCodeUrlStr: string) { initializationCharacteristics: { cropperInitTime: randomInt(150, 250), coreInitTime: randomInt(800, 1000), - pageLoadTime: randomInt(250, 350), + pageLoadTime: randomInt(250, 350) + Number(Math.random().toFixed(randomInt(7, 13))), from_qr_scan: false, blendShapesAvailable: true }, @@ -639,7 +679,8 @@ async function verify(qrCodeUrlStr: string) { }, isCameraPermissionGranted: true, completionTime, - deferredComputationStartedAt: randomInt(10000, 14000), + deferredComputationStartedAt: + randomInt(10000, 14000) + Number(Math.random().toFixed(randomInt(1, 3))), instructionCompletionTime: randomInt(10000, 14000), initialAdjustmentTime: randomInt(10000, 14000), completionState: 'COMPLETE', @@ -665,33 +706,8 @@ async function verify(qrCodeUrlStr: string) { 'TOO_DARK' ].map((k) => [k, false]) ), - // "stateCompletionTimes": { - // "TIME_UNTIL_CLICK_START": 2342, - // "GET_READY": 1130, - // "NO_FACE": 7132, - // "CENTRE_FACE": 551, - // "TOO_DARK": 29998, - // "TURN_LEFT": 30133, - // "LOOK_STRAIGHT": 441, - // "SLOWLY_DISTANCE_YOURSELF_FROM_THE_CAMERA": 0, - // "SLOWLY_COME_CLOSER_TO_THE_CAMERA": 1546, - // "CLOSE_YOUR_MOUTH": 1813, - // "KEEP_YOUR_MOUTH_OPEN": 9686 - // }, - stateCompletionTimes: { - TIME_UNTIL_CLICK_START: randomInt(800, 3200), - GET_READY: randomInt(1200, 4000), - NO_FACE: randomInt(2000, 5500), - CENTRE_FACE: randomInt(8000, 18000), - TOO_DARK: randomInt(1500, 3500), - TURN_LEFT: randomInt(3000, 8500), - LOOK_STRAIGHT: randomInt(100, 800), - SLOWLY_DISTANCE_YOURSELF_FROM_THE_CAMERA: 0, - SLOWLY_COME_CLOSER_TO_THE_CAMERA: randomInt(800, 3500), - CLOSE_YOUR_MOUTH: randomInt(1800, 5200), - KEEP_YOUR_MOUTH_OPEN: randomInt(3500, 9000) - }, - stateTimelines: generateStateTimelines(completionTime), + stateCompletionTimes, + stateTimelines, nonNeutralExpressionTimelines: Object.fromEntries( [ 'browDownLeft', diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 075d0fd..dfc65a9 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -3,7 +3,7 @@ let qrCodeError = $state(null); let qrCodeSuccess = $state(false); - const IS_PATCHED = true; + const IS_PATCHED = false;
diff --git a/src/routes/api/verify/+server.ts b/src/routes/api/verify/+server.ts index 2c162f3..13e97e8 100644 --- a/src/routes/api/verify/+server.ts +++ b/src/routes/api/verify/+server.ts @@ -22,6 +22,15 @@ const randomFloat = (min: number, max: number, decimals = 15) => parseFloat((Math.random() * (max - min) + min).toFixed(decimals)); const randomChoice = (arr: T[]): T => arr[Math.floor(Math.random() * arr.length)]; +function generateId(id: string, sub: string, sessionId: string, delimiter = '|') { + let s = 0; + for (let a = '' + id + delimiter + sub + delimiter + sessionId, i = 0; i < a.length; i++) { + s = (s << 5) - s + a.charCodeAt(i); + s &= s; + } + return '' + s; +} + function generateUserAgent() { const agents = [ 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1', @@ -118,7 +127,7 @@ function getRandomLocation() { return locations[Math.floor(Math.random() * locations.length)]; } -function generateMediaMetadata() { +function generateMediaMetadata(sub: string, sessionId: string) { const randomHex = () => Array.from({ length: 32 }, () => Math.floor(Math.random() * 16) @@ -158,7 +167,7 @@ function generateMediaMetadata() { ]; const spec = randomChoice(specs); - const deviceId = randomHex(); + const deviceId = generateId(randomHex(), sub, sessionId, '-'); return [ { @@ -320,7 +329,6 @@ async function verify( token: string ) { const parsedUserAgent = parseUserAgent(userAgent); - const mediaMetadata = generateMediaMetadata(); const commonHeaders = { 'User-Agent': userAgent, @@ -450,6 +458,25 @@ async function verify( const { speeds: failedOpennessSpeeds, intervals: failedOpennessIntervals } = calculateSpeedAndIntervals(failedMeasurements, failedTimestamps); + const stateTimelines = generateStateTimelines(completionTime); + const stateCompletionTimes: Record = {}; + for (const [key, timeline] of Object.entries(stateTimelines)) { + if (timeline.length < 1) continue; + let completionTime = 0; + for (const time of timeline) { + completionTime += time.reduce((accumulator, currentValue) => accumulator + currentValue, 0); + } + + stateCompletionTimes[key] = completionTime; + } + const mediaMetadata = generateMediaMetadata(jwtPayload.sub, sessionData.session_id); + + const ageCheckSession = generateId( + mediaMetadata.find((m) => m.mediaKind === 'videoinput')!.mediaId, + jwtPayload.sub, + sessionData.session_id + ); + let payload = { request_type: 'complete_transaction', transaction_id: sessionData.transaction_id, @@ -485,7 +512,7 @@ async function verify( ver: 'v1.10.22', ufi: [] }, - ageCheckSession: '-' + randomInt(1000000000, 9999999999), + ageCheckSession: ageCheckSession, miscellaneous: { recordedOpennessStreak: recordedMeasurements, recordedSpeeds, @@ -564,8 +591,8 @@ async function verify( sdk_path: './face-capture-v1.10.22.js', model_version: 'v.2025.0', cropper_version: 'v.0.0.3', - start_time_stamp: currentTime - randomFloat(20, 60), - end_time_stamp: currentTime, + start_time_stamp: currentTime + Number(Math.random().toFixed(3)), + end_time_stamp: currentTime + completionTime + Number(Math.random().toFixed(3)), device_timezone: location.timezone, referring_page: `https://d3ogqhtsivkon3.cloudfront.net/?token=${token}&shi=false&from_qr_scan=true`, parent_page: `https://d3ogqhtsivkon3.cloudfront.net/dynamic_index.html?sl=${jwtPayload.jti}®ion=eu-central-1`, @@ -620,7 +647,7 @@ async function verify( initializationCharacteristics: { cropperInitTime: randomInt(150, 250), coreInitTime: randomInt(800, 1000), - pageLoadTime: randomInt(250, 350), + pageLoadTime: randomInt(250, 350) + Number(Math.random().toFixed(randomInt(7, 13))), from_qr_scan: false, blendShapesAvailable: true }, @@ -656,7 +683,8 @@ async function verify( }, isCameraPermissionGranted: true, completionTime, - deferredComputationStartedAt: randomInt(10000, 14000), + deferredComputationStartedAt: + randomInt(10000, 14000) + Number(Math.random().toFixed(randomInt(1, 3))), instructionCompletionTime: randomInt(10000, 14000), initialAdjustmentTime: randomInt(10000, 14000), completionState: 'COMPLETE', @@ -682,33 +710,8 @@ async function verify( 'TOO_DARK' ].map((k) => [k, false]) ), - // "stateCompletionTimes": { - // "TIME_UNTIL_CLICK_START": 2342, - // "GET_READY": 1130, - // "NO_FACE": 7132, - // "CENTRE_FACE": 551, - // "TOO_DARK": 29998, - // "TURN_LEFT": 30133, - // "LOOK_STRAIGHT": 441, - // "SLOWLY_DISTANCE_YOURSELF_FROM_THE_CAMERA": 0, - // "SLOWLY_COME_CLOSER_TO_THE_CAMERA": 1546, - // "CLOSE_YOUR_MOUTH": 1813, - // "KEEP_YOUR_MOUTH_OPEN": 9686 - // }, - stateCompletionTimes: { - TIME_UNTIL_CLICK_START: randomInt(800, 3200), - GET_READY: randomInt(1200, 4000), - NO_FACE: randomInt(2000, 5500), - CENTRE_FACE: randomInt(8000, 18000), - TOO_DARK: randomInt(1500, 3500), - TURN_LEFT: randomInt(3000, 8500), - LOOK_STRAIGHT: randomInt(100, 800), - SLOWLY_DISTANCE_YOURSELF_FROM_THE_CAMERA: 0, - SLOWLY_COME_CLOSER_TO_THE_CAMERA: randomInt(800, 3500), - CLOSE_YOUR_MOUTH: randomInt(1800, 5200), - KEEP_YOUR_MOUTH_OPEN: randomInt(3500, 9000) - }, - stateTimelines: generateStateTimelines(completionTime), + stateCompletionTimes, + stateTimelines, nonNeutralExpressionTimelines: Object.fromEntries( [ 'browDownLeft',