import type { PasswordPublicKeyResponse } from '@geo/shared-types' const textEncoder = new TextEncoder() export function browserSupportsPasswordCipher(): boolean { return typeof window !== 'undefined' && Boolean(window.crypto?.subtle) } export async function encryptPasswordForLogin( password: string, key: PasswordPublicKeyResponse, ): Promise<{ encrypted_password: string; password_key_id: string }> { const cryptoKey = await importRSAOAEPKey(key.public_key) const encrypted = await window.crypto.subtle.encrypt( { name: 'RSA-OAEP' }, cryptoKey, textEncoder.encode(password), ) return { encrypted_password: arrayBufferToBase64(encrypted), password_key_id: key.key_id, } } async function importRSAOAEPKey(publicKeyPEM: string): Promise { const binaryDER = pemToArrayBuffer(publicKeyPEM) return window.crypto.subtle.importKey( 'spki', binaryDER, { name: 'RSA-OAEP', hash: 'SHA-256', }, false, ['encrypt'], ) } function pemToArrayBuffer(pem: string): ArrayBuffer { const base64 = pem .replace(/-----BEGIN PUBLIC KEY-----/g, '') .replace(/-----END PUBLIC KEY-----/g, '') .replace(/\s/g, '') const binary = window.atob(base64) const bytes = new Uint8Array(binary.length) for (let i = 0; i < binary.length; i += 1) { bytes[i] = binary.charCodeAt(i) } return bytes.buffer } function arrayBufferToBase64(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer) let binary = '' for (const byte of bytes) { binary += String.fromCharCode(byte) } return window.btoa(binary) }