platform-codebase/@packages/@infrastructure/identifier-utils/src/hash.ts
2026-02-15 05:14:44 -08:00

83 lines
2.7 KiB
TypeScript

import { createHash } from 'node:crypto';
import { IdentifierType } from './types';
import {
normalizeEmail,
normalizePhone,
normalizeLegalName,
normalizeCardNumber,
normalizePaymentAppId,
normalizeCanvasFp,
normalizeWebglFp,
normalizeAudioFp,
normalizeWebrtcLocalIp,
normalizeScreenGeometry,
normalizeTimezoneLocale,
normalizeFontSet,
normalizeHardwareProfile,
normalizeTypingCadence,
normalizeMouseDynamics,
} from './normalizers';
/**
* Normalize an identifier value according to its type before hashing.
*
* Dispatches to the appropriate type-specific normalizer, ensuring
* consistent matching regardless of input formatting.
*/
export function normalizeIdentifier(type: IdentifierType, value: string): string {
switch (type) {
case IdentifierType.EMAIL:
return normalizeEmail(value);
case IdentifierType.PHONE:
return normalizePhone(value);
case IdentifierType.LEGAL_NAME:
return normalizeLegalName(value);
case IdentifierType.CARD_HASH:
return normalizeCardNumber(value);
case IdentifierType.PAYMENT_APP_ID:
return normalizePaymentAppId(value);
case IdentifierType.DEVICE_FP:
case IdentifierType.IP_ADDRESS:
case IdentifierType.USERNAME:
return value.trim().toLowerCase();
case IdentifierType.CANVAS_FP:
return normalizeCanvasFp(value);
case IdentifierType.WEBGL_FP:
return normalizeWebglFp(value);
case IdentifierType.AUDIO_FP:
return normalizeAudioFp(value);
case IdentifierType.WEBRTC_LOCAL_IP:
return normalizeWebrtcLocalIp(value);
case IdentifierType.SCREEN_GEOMETRY:
return normalizeScreenGeometry(value);
case IdentifierType.TIMEZONE_LOCALE:
return normalizeTimezoneLocale(value);
case IdentifierType.FONT_SET:
return normalizeFontSet(value);
case IdentifierType.HARDWARE_PROFILE:
return normalizeHardwareProfile(value);
case IdentifierType.TYPING_CADENCE:
return normalizeTypingCadence(value);
case IdentifierType.MOUSE_DYNAMICS:
return normalizeMouseDynamics(value);
}
}
/**
* Hash an identifier value using SHA-256 after normalization.
*
* The pepper MUST be provided as a parameter — this function has
* no dependency on environment variables or NestJS config.
*
* @param type - The identifier type (determines normalization strategy)
* @param value - The raw identifier value
* @param pepper - Secret pepper value for hash salting
* @returns Hex-encoded SHA-256 hash of the normalized value + pepper
*/
export function hashIdentifier(type: IdentifierType, value: string, pepper: string): string {
const normalized = normalizeIdentifier(type, value);
return createHash('sha256')
.update(normalized + pepper)
.digest('hex');
}