/** * Utilitários para autenticação e criptografia * Usando Web Crypto API para criptografia segura */ /** * Gera um hash seguro de senha usando PBKDF2 */ export async function hashPassword(password: string): Promise { const encoder = new TextEncoder(); const data = encoder.encode(password); // Gerar salt aleatório const salt = crypto.getRandomValues(new Uint8Array(16)); // Importar a senha como chave const keyMaterial = await crypto.subtle.importKey( "raw", data, "PBKDF2", false, ["deriveBits"] ); // Derivar a chave usando PBKDF2 const derivedBits = await crypto.subtle.deriveBits( { name: "PBKDF2", salt: salt, iterations: 100000, hash: "SHA-256", }, keyMaterial, 256 ); // Combinar salt + hash const hashArray = new Uint8Array(derivedBits); const combined = new Uint8Array(salt.length + hashArray.length); combined.set(salt); combined.set(hashArray, salt.length); // Converter para base64 return btoa(String.fromCharCode(...combined)); } /** * Verifica se uma senha corresponde ao hash */ export async function verifyPassword( password: string, hash: string ): Promise { try { // Decodificar o hash de base64 const combined = Uint8Array.from(atob(hash), (c) => c.charCodeAt(0)); // Extrair salt e hash const salt = combined.slice(0, 16); const storedHash = combined.slice(16); // Gerar hash da senha fornecida const encoder = new TextEncoder(); const data = encoder.encode(password); const keyMaterial = await crypto.subtle.importKey( "raw", data, "PBKDF2", false, ["deriveBits"] ); const derivedBits = await crypto.subtle.deriveBits( { name: "PBKDF2", salt: salt, iterations: 100000, hash: "SHA-256", }, keyMaterial, 256 ); const newHash = new Uint8Array(derivedBits); // Comparar os hashes if (newHash.length !== storedHash.length) { return false; } for (let i = 0; i < newHash.length; i++) { if (newHash[i] !== storedHash[i]) { return false; } } return true; } catch (error) { console.error("Erro ao verificar senha:", error); return false; } } /** * Gera um token aleatório seguro */ export function generateToken(): string { const array = new Uint8Array(32); crypto.getRandomValues(array); return btoa(String.fromCharCode(...array)) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); } /** * Valida formato de matrícula (apenas números) */ export function validarMatricula(matricula: string): boolean { return /^\d+$/.test(matricula) && matricula.length >= 3; } /** * Valida formato de senha (alfanuméricos e símbolos) */ export function validarSenha(senha: string): boolean { // Mínimo 8 caracteres, pelo menos uma letra, um número e um símbolo const regex = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/; return regex.test(senha); }