// Web-specific implementation using web-encryption library import * as WebEncryption from 'web-secure-encryption'; declare var crypto: any; declare var atob: (input: string) => string; declare var btoa: (input: string) => string; declare var TextEncoder: { new (): { encode(input: string): Uint8Array } }; declare var TextDecoder: { new (label?: string): { decode(input: ArrayBuffer): string }; }; export const generateAESKey = WebEncryption.generateAESKey; export const encryptAES = WebEncryption.encryptAES; export const decryptAES = WebEncryption.decryptAES; export const encryptAsyncAES = WebEncryption.encryptAsyncAES; export const decryptAsyncAES = WebEncryption.decryptAsyncAES; export const encryptRSA = WebEncryption.encryptRSA; export const decryptRSA = WebEncryption.decryptRSA; export const encryptAsyncRSA = WebEncryption.encryptAsyncRSA; export const decryptAsyncRSA = WebEncryption.decryptAsyncRSA; export const generateRSAKeyPair = WebEncryption.generateRSAKeyPair; export const generateECDSAKeyPair = WebEncryption.generateECDSAKeyPair; export const signDataECDSA = WebEncryption.signDataECDSA; export const verifySignatureECDSA = WebEncryption.verifySignatureECDSA; export const base64Encode = WebEncryption.base64Encode; export const base64Decode = WebEncryption.base64Decode; export const getPublicECDSAKey = WebEncryption.getPublicECDSAKey; // --- Helpers --- function arrayBufferToHex(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); let hex = ''; for (let i = 0; i < bytes.byteLength; i++) { hex += bytes[i]!.toString(16).padStart(2, '0'); } return hex; } function base64ToArrayBuffer(base64: string): ArrayBuffer { const cleaned = base64.replace(/\s/g, ''); const binaryString: string = atob(cleaned); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer as ArrayBuffer; } function arrayBufferToBase64(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]!); } return btoa(binary); } function stringToUtf8Bytes(str: string): Uint8Array { return new TextEncoder().encode(str); } // --- Hash: output hex to match native --- export async function hashSHA256(input: string): Promise { const data = stringToUtf8Bytes(input); const hashBuffer = await crypto.subtle.digest('SHA-256', data); return arrayBufferToHex(hashBuffer); } export async function hashSHA512(input: string): Promise { const data = stringToUtf8Bytes(input); const hashBuffer = await crypto.subtle.digest('SHA-512', data); return arrayBufferToHex(hashBuffer); } // --- HMAC: decode Base64 key and output hex to match native --- export async function generateHMACKey(keySize: number): Promise { const key = await crypto.subtle.generateKey( { name: 'HMAC', hash: 'SHA-256', length: keySize }, true, ['sign'] ); const rawKey = await crypto.subtle.exportKey('raw', key); const bytes = new Uint8Array(rawKey); let binary = ''; for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]!); } return btoa(binary); } export async function hmacSHA256(data: string, key: string): Promise { const keyBytes = base64ToArrayBuffer(key); const cryptoKey = await crypto.subtle.importKey( 'raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); const dataBytes = stringToUtf8Bytes(data); const signature = await crypto.subtle.sign('HMAC', cryptoKey, dataBytes); return arrayBufferToHex(signature); } export async function hmacSHA512(data: string, key: string): Promise { const keyBytes = base64ToArrayBuffer(key); const cryptoKey = await crypto.subtle.importKey( 'raw', keyBytes, { name: 'HMAC', hash: 'SHA-512' }, false, ['sign'] ); const dataBytes = stringToUtf8Bytes(data); const signature = await crypto.subtle.sign('HMAC', cryptoKey, dataBytes); return arrayBufferToHex(signature); } // --- generateRandomString: produce alphanumeric string to match native --- export async function generateRandomString(length: number): Promise { const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const randomValues = new Uint8Array(length); crypto.getRandomValues(randomValues); let result = ''; for (let i = 0; i < length; i++) { result += charset[randomValues[i]! % charset.length]; } return result; } // --- getPublicRSAkey: extract public key from private key --- export async function getPublicRSAkey( privateKeyBase64: string ): Promise { const keyData = base64ToArrayBuffer(privateKeyBase64); const privateKey = await crypto.subtle.importKey( 'pkcs8', keyData, { name: 'RSA-OAEP', hash: 'SHA-256' }, true, ['decrypt'] ); const jwk = await crypto.subtle.exportKey('jwk', privateKey); const publicJwk = { kty: jwk.kty, n: jwk.n, e: jwk.e, alg: jwk.alg, ext: true, key_ops: ['encrypt'], }; const publicKey = await crypto.subtle.importKey( 'jwk', publicJwk, { name: 'RSA-OAEP', hash: 'SHA-256' }, true, ['encrypt'] ); const spkiBuffer = await crypto.subtle.exportKey('spki', publicKey); const spkiBytes = new Uint8Array(spkiBuffer); let binary = ''; for (let i = 0; i < spkiBytes.byteLength; i++) { binary += String.fromCharCode(spkiBytes[i]!); } return btoa(binary); } // --- RSA Sign/Verify --- export async function signDataRSA( data: string, privateKeyBase64: string ): Promise { const keyData = base64ToArrayBuffer(privateKeyBase64); const privateKey = await crypto.subtle.importKey( 'pkcs8', keyData, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['sign'] ); const dataBytes = stringToUtf8Bytes(data); const signature: ArrayBuffer = await crypto.subtle.sign( 'RSASSA-PKCS1-v1_5', privateKey, dataBytes ); return arrayBufferToBase64(signature); } export async function verifySignatureRSA( data: string, signatureBase64: string, publicKeyBase64: string ): Promise { const keyData = base64ToArrayBuffer(publicKeyBase64); const publicKey = await crypto.subtle.importKey( 'spki', keyData, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['verify'] ); const signatureBytes = base64ToArrayBuffer(signatureBase64); const dataBytes = stringToUtf8Bytes(data); const result: boolean = await crypto.subtle.verify( 'RSASSA-PKCS1-v1_5', publicKey, signatureBytes, dataBytes ); return result; } // --- PBKDF2 Key Derivation --- export async function pbkdf2( password: string, salt: string, iterations: number, keyLength: number, hash: string ): Promise { const enc = new TextEncoder(); const keyMaterial = await crypto.subtle.importKey( 'raw', enc.encode(password), 'PBKDF2', false, ['deriveBits'] ); const hashAlgo = hash === 'SHA-512' ? 'SHA-512' : 'SHA-256'; const derivedBits = await crypto.subtle.deriveBits( { name: 'PBKDF2', salt: enc.encode(salt), iterations: iterations, hash: hashAlgo, }, keyMaterial, keyLength * 8 ); return arrayBufferToBase64(derivedBits); } // --- Secure Random Bytes --- export async function getRandomBytes(size: number): Promise { const bytes = new Uint8Array(size); crypto.getRandomValues(bytes); return arrayBufferToBase64(bytes.buffer as ArrayBuffer); } // --- RSA-OAEP Encryption --- export async function encryptRSAOAEP( data: string, publicKeyBase64: string ): Promise { const keyData = base64ToArrayBuffer(publicKeyBase64); const publicKey = await crypto.subtle.importKey( 'spki', keyData, { name: 'RSA-OAEP', hash: 'SHA-256' }, false, ['encrypt'] ); const enc = new TextEncoder(); const encrypted = await crypto.subtle.encrypt( { name: 'RSA-OAEP' }, publicKey, enc.encode(data) ); return arrayBufferToBase64(encrypted); } export async function decryptRSAOAEP( data: string, privateKeyBase64: string ): Promise { const keyData = base64ToArrayBuffer(privateKeyBase64); const privateKey = await crypto.subtle.importKey( 'pkcs8', keyData, { name: 'RSA-OAEP', hash: 'SHA-256' }, false, ['decrypt'] ); const encryptedData = base64ToArrayBuffer(data); const decrypted = await crypto.subtle.decrypt( { name: 'RSA-OAEP' }, privateKey, encryptedData ); return new TextDecoder('utf-8').decode(decrypted); }