export type PortableCryptoKey = Uint8Array;
export type PortableCryptoKeyPair = Record<keyof CryptoKeyPair, PortableCryptoKey>;
const sizeKey = 32;const sizeTag = 16;const sizeIv = 12;
const nameEc = "P-384";const nameMac = "SHA-384";
async function deriveSecretKey(kp:PortableCryptoKeyPair){ const ec:EcKeyAlgorithm = { name: "ECDH", namedCurve: nameEc };
const aes:AesDerivedKeyParams = { name: "AES-GCM", length: sizeKey * 8 };
const publicKey = await crypto.subtle.importKey("spki", kp.publicKey, ec, false, []); const privateKey = await crypto.subtle.importKey("pkcs8", kp.privateKey, ec, false, ["deriveKey", "deriveBits"]);
const dh:EcdhKeyDeriveParams = { name: "ECDH", public: publicKey };
return await crypto.subtle.deriveKey(dh, privateKey, aes, false, ["encrypt", "decrypt"]);}
export async function cryptoRandom(size:number){ return await new Promise<Uint8Array>(done => done(crypto.getRandomValues(new Uint8Array(size))));}
export async function cryptoHash(is512:boolean, data:Uint8Array){ const sha = is512 ? "SHA-512" : "SHA-256";
return new Uint8Array(await crypto.subtle.digest(sha, data));}
export async function cryptoGenerateKey(isECDH:boolean){ const usage:KeyUsage[] = isECDH ? ["deriveKey", "deriveBits"] : ["sign", "verify"];
const ec:EcKeyAlgorithm = { name: isECDH ? "ECDH" : "ECDSA", namedCurve: nameEc };
const {publicKey, privateKey} = await crypto.subtle.generateKey(ec, true, usage);
return <PortableCryptoKeyPair>{ publicKey: new Uint8Array(await crypto.subtle.exportKey("spki", publicKey)), privateKey: new Uint8Array(await crypto.subtle.exportKey("pkcs8", privateKey)) };}
export async function cryptoEncrypt(kp:PortableCryptoKeyPair, data:Uint8Array){ const gcm:AesGcmParams = { name: "AES-GCM", tagLength: sizeTag * 8, iv: await cryptoRandom(sizeIv) };
const secretKey = await deriveSecretKey(kp);
const output = new Uint8Array(sizeTag + sizeIv + data.byteLength); output.set(<Uint8Array>gcm.iv, 0); output.set(new Uint8Array(await crypto.subtle.encrypt(gcm, secretKey, data)), gcm.iv.byteLength);
return output;}
export async function cryptoDecrypt(kp:PortableCryptoKeyPair, data:Uint8Array){ const gcm:AesGcmParams = { name: "AES-GCM", tagLength: sizeTag * 8, iv: data.subarray(0, sizeIv) };
const secretKey = await deriveSecretKey(kp);
return new Uint8Array(await crypto.subtle.decrypt(gcm, secretKey, data.subarray(sizeIv)));}
export async function cryptoSign(k:PortableCryptoKey, data:Uint8Array){ const ec:EcKeyAlgorithm = { name: "ECDSA", namedCurve: nameEc };
const dsa:EcdsaParams = { name: "ECDSA", hash: { name: nameMac } };
const privateKey = await crypto.subtle.importKey("pkcs8", k, ec, false, ["sign"]);
return new Uint8Array(await crypto.subtle.sign(dsa, privateKey, data));}
export async function cryptoVerify(signature:Uint8Array, k:PortableCryptoKey, data:Uint8Array){ const ec:EcKeyAlgorithm = { name: "ECDSA", namedCurve: nameEc };
const dsa:EcdsaParams = { name: "ECDSA", hash: { name: nameMac } };
const publicKey = await crypto.subtle.importKey("spki", k, ec, false, ["verify"]);
return await crypto.subtle.verify(dsa, publicKey, signature, data);}