// This module contains browser-specific WebCrypto API utilities // Check if we're in a browser environment export const isBrowser = (): boolean => typeof window !== 'undefined'; // Use the polyfill if available, otherwise fall back to native WebCrypto const getCrypto = (): Crypto => { if (typeof window !== 'undefined' && window.crypto) { return window.crypto; } // Fallback to native WebCrypto if polyfill is not available return window.crypto; }; // Get registered users from localStorage export const getRegisteredUsers = (): string[] => { if (!isBrowser()) return []; try { return JSON.parse(window.localStorage.getItem('registeredUsers') || '[]'); } catch (error) { console.error('Error getting registered users:', error); return []; } }; // Add a user to the registered users list export const addRegisteredUser = (username: string): void => { if (!isBrowser()) return; try { const users = getRegisteredUsers(); if (!users.includes(username)) { users.push(username); window.localStorage.setItem('registeredUsers', JSON.stringify(users)); } } catch (error) { console.error('Error adding registered user:', error); } }; // Check if a username is available export const isUsernameAvailable = async (username: string): Promise => { try { // Get the list of registered users const users = getRegisteredUsers(); // Check if the username is already taken const isAvailable = !users.includes(username); return isAvailable; } catch (error) { console.error('Error checking username availability:', error); return false; } }; // Check if username is valid format (letters, numbers, underscores, hyphens) export const isUsernameValid = (username: string): boolean => { const usernameRegex = /^[a-zA-Z0-9_-]{3,20}$/; return usernameRegex.test(username); }; // Store a public key for a user export const storePublicKey = (username: string, publicKey: string): void => { if (!isBrowser()) return; try { window.localStorage.setItem(`${username}_publicKey`, publicKey); } catch (error) { console.error('Error storing public key:', error); } }; // Get a user's public key export const getPublicKey = (username: string): string | null => { if (!isBrowser()) return null; try { return window.localStorage.getItem(`${username}_publicKey`); } catch (error) { console.error('Error getting public key:', error); return null; } }; // Generate a key pair using Web Crypto API export const generateKeyPair = async (): Promise => { if (!isBrowser()) return null; try { const crypto = getCrypto(); return await crypto.subtle.generateKey( { name: 'ECDSA', namedCurve: 'P-256', }, true, ['sign', 'verify'] ); } catch (error) { console.error('Error generating key pair:', error); return null; } }; // Export a public key to a base64 string export const exportPublicKey = async (publicKey: CryptoKey): Promise => { if (!isBrowser()) return null; try { const crypto = getCrypto(); const publicKeyBuffer = await crypto.subtle.exportKey( 'raw', publicKey ); return btoa( String.fromCharCode.apply(null, Array.from(new Uint8Array(publicKeyBuffer))) ); } catch (error) { console.error('Error exporting public key:', error); return null; } }; // Import a public key from a base64 string export const importPublicKey = async (base64Key: string): Promise => { if (!isBrowser()) return null; try { const crypto = getCrypto(); const binaryString = atob(base64Key); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return await crypto.subtle.importKey( 'raw', bytes, { name: 'ECDSA', namedCurve: 'P-256', }, true, ['verify'] ); } catch (error) { console.error('Error importing public key:', error); return null; } }; // Sign data with a private key export const signData = async (privateKey: CryptoKey, data: string): Promise => { if (!isBrowser()) return null; try { const crypto = getCrypto(); const encoder = new TextEncoder(); const encodedData = encoder.encode(data); const signature = await crypto.subtle.sign( { name: 'ECDSA', hash: { name: 'SHA-256' }, }, privateKey, encodedData ); return btoa( String.fromCharCode.apply(null, Array.from(new Uint8Array(signature))) ); } catch (error) { console.error('Error signing data:', error); return null; } }; // Verify a signature export const verifySignature = async ( publicKey: CryptoKey, signature: string, data: string ): Promise => { if (!isBrowser()) return false; try { const crypto = getCrypto(); const encoder = new TextEncoder(); const encodedData = encoder.encode(data); const binarySignature = atob(signature); const signatureBytes = new Uint8Array(binarySignature.length); for (let i = 0; i < binarySignature.length; i++) { signatureBytes[i] = binarySignature.charCodeAt(i); } return await crypto.subtle.verify( { name: 'ECDSA', hash: { name: 'SHA-256' }, }, publicKey, signatureBytes, encodedData ); } catch (error) { console.error('Error verifying signature:', error); return false; } };