export type PortableCryptoKey = Uint8Array;
export type PortableCryptoKeyPair = Record<keyof CryptoKeyPair, PortableCryptoKey>;
const sizeTag = 16;const sizeIv = 12;
function generateAesGcmConfig(tag:number, iv:Uint8Array){ return <AesGcmParams>{ name: "AES-GCM", tagLength: tag * 8, iv: iv };}
function generateEcKeyConfig(isECDH:boolean){ return <EcKeyAlgorithm>{ name: isECDH ? "ECDH" : "ECDSA", namedCurve: "P-521" };}
function generateEcDsaConfig(){ return <EcdsaParams>{ name: "ECDSA", hash: { name: "SHA-512" } };}
async function deriveSecretKey(kp:PortableCryptoKeyPair){ const ec = generateEcKeyConfig(true); 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 aes:AesDerivedKeyParams = { name: "AES-GCM", length: 256 };
const dh:EcdhKeyDeriveParams = { name: "ECDH", public: publicKey };
return await crypto.subtle.deriveKey(dh, privateKey, aes, false, ["encrypt", "decrypt"]);}
export function cryptoUuid(){ return crypto.randomUUID();}
export function cryptoRandom(size:number){ return 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 = generateEcKeyConfig(isECDH); 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 = generateAesGcmConfig(sizeTag, 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 = generateAesGcmConfig(sizeTag, 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 = generateEcKeyConfig(false); const dsa = generateEcDsaConfig(); 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 = generateEcKeyConfig(false); const dsa = generateEcDsaConfig(); const publicKey = await crypto.subtle.importKey("spki", k, ec, false, ["verify"]);
return await crypto.subtle.verify(dsa, publicKey, signature, data);}