import { signedText } from "../shared/bundle"; import { denylistSignedText, jobSignedText, type Denylist } from "../shared/envelope"; /** * Signing, for whoever holds a private key: the release key (a person's machine, never the serving host) signs bundles and the revocation list; the dispatch key (the * central service) signs each job. The private key never goes into the extension; only the public half is pinned in its build. */ export async function generateSigningKey(): Promise<{ privateKey: CryptoKey; publicKey: string }> { const pair = (await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"])) as CryptoKeyPair; const raw = new Uint8Array(await crypto.subtle.exportKey("raw", pair.publicKey)); return { privateKey: pair.privateKey, publicKey: Buffer.from(raw).toString("base64") }; } const sign = async (key: CryptoKey, data: Uint8Array): Promise => Buffer.from(await crypto.subtle.sign("Ed25519", key, data as BufferSource)).toString("base64"); /** Signs a bundle's hash together with the time (milliseconds since 1970) after which no device should run it. */ export const signBundle = (hash: string, notAfter: number, privateKey: CryptoKey): Promise => sign(privateKey, signedText(hash, notAfter)); export const signJob = (job: Record, privateKey: CryptoKey): Promise => sign(privateKey, jobSignedText(job)); export const signDenylist = (list: Denylist, privateKey: CryptoKey): Promise => sign(privateKey, denylistSignedText(list));