export class CryptoUtil { static algorithm = 'RSASSA-PKCS1-v1_5'; static hash = 'SHA-256'; static async generateAsymmetricKey() { return window.crypto.subtle.generateKey( { name: CryptoUtil.algorithm, hash: CryptoUtil.hash, modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), }, true, ['sign', 'verify'], ); } static async sign(content, privateKey) { const encoder = new TextEncoder(); const encoded = encoder.encode(content); const signature = await window.crypto.subtle.sign(CryptoUtil.algorithm, privateKey, encoded); // Return base64-encoded signature return btoa(String.fromCharCode(...new Uint8Array(signature))); } static async verify(content, b64publicKey, b64signature) { // Convert base64 javascript web key to CryptoKey const publicKey = await CryptoUtil.importKey(b64publicKey); // Convert base64 signature to an ArrayBuffer const signature = Uint8Array.from(atob(b64signature), (c) => c.charCodeAt(0)); // TODO: make a single TextEncoder instance and reuse it const encoder = new TextEncoder(); const encoded = encoder.encode(content); return window.crypto.subtle.verify(CryptoUtil.algorithm, publicKey, signature, encoded); } static async exportKey(publicKey) { // Store public key as base64 javascript web key const jwk = await window.crypto.subtle.exportKey('jwk', publicKey); return btoa(JSON.stringify(jwk)); } static async importKey(b64jwk) { // Convert base64 javascript web key to CryptoKey const jwk = JSON.parse(atob(b64jwk)); return window.crypto.subtle.importKey( 'jwk', jwk, { name: CryptoUtil.algorithm, hash: CryptoUtil.hash, }, false, ['verify'], ); } static randomUUID() { return window.crypto.randomUUID(); } }