import { createHash } from "node:crypto"; /** * Normalisiert eine Domain für Hashing — muss zwischen Server und iOS-Extension * IDENTISCH sein, sonst stimmen die Hashes nicht überein. * * Schritte: * 1. trim, lowercase * 2. http:// und https:// entfernen * 3. Pfad / Query nach erstem `/` abschneiden * 4. Optional `www.` Prefix entfernen (so dass `www.bet365.com` und `bet365.com` * den gleichen Hash haben) */ export function normalizeDomain(input: string): string { return input .trim() .toLowerCase() .replace(/^https?:\/\//, "") .replace(/\/.*$/, "") .replace(/^www\./, ""); } /** * SHA-256 → erste 8 Bytes als big-endian UInt64. * Wenn salt gesetzt ist, wird `:` gehasht (für user-spezifische * Custom-Domains, damit gleiche Domain bei zwei Usern unterschiedliche * Hashes ergibt → keine Cross-User-Korrelation möglich). */ export function hashDomain(domain: string, salt = ""): bigint { const normalized = normalizeDomain(domain); const input = salt ? `${salt}:${normalized}` : normalized; const digest = createHash("sha256").update(input, "utf8").digest(); return digest.readBigUInt64BE(0); } /** * Hasht eine Liste von Domains, sortiert die Hashes aufsteigend, und gibt * sie als Binary-Buffer zurück (8 Bytes pro Hash, big-endian). * * Format der Binary-Datei für die iOS-Extension: * ┌────────────────┬────────────────┬─────────────────┐ * │ Hash 0 (8 B) │ Hash 1 (8 B) │ Hash 2 (8 B) │ ... * └────────────────┴────────────────┴─────────────────┘ * sorted ascending → binary-search möglich, O(log n). */ export function buildHashListBinary(domains: string[], salt = ""): Buffer { const hashes = new BigUint64Array(domains.length); for (let i = 0; i < domains.length; i++) { hashes[i] = hashDomain(domains[i], salt); } hashes.sort(); // BigUint64Array ist platform-endian — wir brauchen explicit big-endian // damit Server und iOS-Extension dasselbe Format lesen. const buf = Buffer.alloc(hashes.length * 8); for (let i = 0; i < hashes.length; i++) { buf.writeBigUInt64BE(hashes[i], i * 8); } return buf; } /** * ETag aus dem Binary-Content (sha256 der Bytes, hex-encoded). * Client cached darauf basierend → wenn Server-DB sich nicht ändert, * spart Bandbreite + Battery. */ export function etagFor(buf: Buffer): string { return `"${createHash("sha256").update(buf).digest("hex").slice(0, 16)}"`; }