export function hasWebCryptoAPI(): boolean { return !!(window && window.crypto && window.crypto.subtle) } export function hasWebCryptoAPI2(): boolean { return !!(window?.crypto?.subtle) } export async function generateECDHKeyPair() { const key = await window.crypto.subtle.generateKey( { name: 'ECDH', namedCurve: 'P-256', }, /* extractable */ true, ['deriveKey'], ) return key } export async function deriveECDHKey(params: { privateKey: CryptoKey publicKey: CryptoKey }) { const { privateKey, publicKey } = params const derivedKey = await window.crypto.subtle.deriveKey( { name: 'ECDH', public: publicKey, }, privateKey, { name: 'AES-CTR', length: 256, }, /* extractable */ true, ['encrypt', 'decrypt'], ) return derivedKey } export async function encrypt(key: CryptoKey, data: string): Promise { const encrypted = await window.crypto.subtle.encrypt( { name: 'AES-CTR', counter: new Uint8Array(16), length: 128, }, key, str2ab(data), ) return ab2str(encrypted) } export async function decrypt(key: CryptoKey, data: string): Promise { const arrayBuffer = str2ab(data) const decrypted = await window.crypto.subtle.decrypt( { name: 'AES-CTR', counter: new ArrayBuffer(16), length: 128, }, key, arrayBuffer, ) return ab2str(decrypted) } export async function exportKey(key: CryptoKey) { return await window.crypto.subtle.exportKey('jwk', key) } function ab2str( buf: ArrayBuffer, ArrayType: typeof Uint16Array | typeof Uint8Array = Uint16Array, ): string { return String.fromCharCode.apply( null, new ArrayType(buf) as unknown as number[]) } export async function importKey(keyData: JsonWebKey) { if (keyData.kty !== 'EC' || keyData.crv !== 'P-256') { throw new Error(`Unsupported key type: ${keyData.kty}, crv: ${keyData.crv}`) } const key = await window.crypto.subtle.importKey( 'jwk', keyData, { name: 'ECDH', namedCurve: keyData.crv, }, /* extractable */ true, keyData.key_ops || [], ) return key } function str2ab(str: string): ArrayBuffer { const buf = new ArrayBuffer(str.length*2) // 2 bytes for each char const bufView = new Uint16Array(buf) for (let i=0, strLen=str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i) } return buf }