import {byteConcat} from "./byte.ts";
export type PortableCryptoKeyPair = Record<keyof CryptoKeyPair, Uint8Array>;
const AES_MODE = "AES-GCM";const AES_BIT = 128;const FORMAT_PUB = "spki";const FORMAT_PRI = "pkcs8";
const CURVE_ECDH = Object.freeze({ name: "ECDH", namedCurve: "P-256"});
const CURVE_ECDSA = Object.freeze({ name: "ECDSA", namedCurve: "P-256"});
const MAC_ECDSA = Object.freeze({ name: "ECDSA", hash: "SHA-256"});
async function generateKey(alg:EcKeyAlgorithm, usage:KeyUsage[]){ const {publicKey, privateKey} = await crypto.subtle.generateKey(alg, true, usage);
return { publicKey: new Uint8Array(await crypto.subtle.exportKey(FORMAT_PUB, publicKey)), privateKey: new Uint8Array(await crypto.subtle.exportKey(FORMAT_PRI, privateKey)) };}
async function deriveKey({publicKey, privateKey}:PortableCryptoKeyPair){ return await crypto.subtle.deriveKey({ name: CURVE_ECDH.name, public: await crypto.subtle.importKey(FORMAT_PUB, publicKey, CURVE_ECDH, false, []) }, await crypto.subtle.importKey(FORMAT_PRI, privateKey, CURVE_ECDH, false, ["deriveKey"]), { name: AES_MODE, length: AES_BIT }, false, ["encrypt", "decrypt"]);}
export function generateRandom(n:number):Uint8Array{ return crypto.getRandomValues(new Uint8Array(n));}
export async function deriveHash(data:Uint8Array, sha?:`SHA-${256 | 384 | 512}`):Promise<Uint8Array>{ return new Uint8Array(await crypto.subtle.digest(sha ?? "SHA-256", data));}
export async function pkGenerateECDH():Promise<PortableCryptoKeyPair>{ return await generateKey(CURVE_ECDH, ["deriveKey"]);}
export async function pkGenerateECDSA():Promise<PortableCryptoKeyPair>{ return await generateKey(CURVE_ECDSA, ["sign", "verify"]);}
export async function pkEncrypt(data:Uint8Array, {publicKey, privateKey}:PortableCryptoKeyPair):Promise<Uint8Array>{ const aes = { name: AES_MODE, iv: generateRandom(12) };
return byteConcat(aes.iv, await crypto.subtle.encrypt(aes, await deriveKey({publicKey, privateKey}), data));}
export async function pkDecrypt(data:Uint8Array, {publicKey, privateKey}:PortableCryptoKeyPair):Promise<Uint8Array>{ const aes = { name: AES_MODE, iv: data.subarray(0, 12) };
return new Uint8Array(await crypto.subtle.decrypt(aes, await deriveKey({publicKey, privateKey}), data.subarray(aes.iv.byteLength)));}
export async function pkSign(data:Uint8Array, key:Uint8Array):Promise<Uint8Array>{ return new Uint8Array(await crypto.subtle.sign(MAC_ECDSA, await crypto.subtle.importKey(FORMAT_PRI, key, CURVE_ECDSA, false, ["sign"]), data));}
export async function pkVerify(data:Uint8Array, key:Uint8Array, sign:Uint8Array):Promise<boolean>{ return await crypto.subtle.verify(MAC_ECDSA, await crypto.subtle.importKey(FORMAT_PUB, key, CURVE_ECDSA, false, ["verify"]), sign, data);}