// ST-1002: Pure crypto-helpers voor de QR-pairing flow (M10). // // Twee gescheiden 256-bit geheimen per pairing: // mobileSecret — bewijs dat de mobiel komt vanaf het scan-kanaal (QR-fragment → POST-body) // desktopToken — bewijs dat de desktop is wie de pairing startte (HttpOnly cookie) // // In de DB staan alleen sha256-hashes van beide; de plaintext-waarden verlaten // alleen de desktop-JS (mobileSecret via QR-fragment, desktopToken via Set-Cookie) // en blijven nooit in URL-paden of access-logs. import { createHash, randomBytes, timingSafeEqual } from 'crypto' const SECRET_BYTES = 32 export function generateMobileSecret(): string { return randomBytes(SECRET_BYTES).toString('base64url') } export function generateDesktopToken(): string { return randomBytes(SECRET_BYTES).toString('base64url') } export function hashToken(token: string): string { return createHash('sha256').update(token).digest('hex') } export function verifyToken(token: string, hash: string): boolean { const a = Buffer.from(hashToken(token), 'hex') const b = Buffer.from(hash, 'hex') if (a.length !== b.length) return false return timingSafeEqual(a, b) } // Geëxtraheerd zodat de Server Component (app/(app)/layout.tsx) Date.now() niet // rechtstreeks in render aanroept — de React Compiler markeert dat als impure. export function isPairedSessionExpired(session: { paired?: boolean pairedExpiresAt?: number }): boolean { if (!session.paired || !session.pairedExpiresAt) return false return session.pairedExpiresAt < Date.now() }