/* RFC-0558: Identity credential signing and verification — canonicalization and sign/verify wrappers for SiteOwnershipCredential and ActorDelegationCredential subjects, built on the existing Ed25519 signBytes/verifyBytes primitives. Do not handle key storage or management — private keys arrive as env vars. Do not assemble full W3C VC envelopes — callers attach the proof after signing. Do not modify sign.ts — this module wraps signBytes/verifyBytes, not signCredential. RFC-0558: initial identity credential signing module. */ /** * @warpgogol/werkstatt-shared/passport/identity-sign — Identity credential signing (RFC-0558) * * Uses signBytes/verifyBytes from sign.ts with a dedicated canonicalization * function for identity credential subjects. signCredential is NOT reused — * it is typed to CredentialSubjectDigest (build provenance fields) and cannot * accept identity credential subjects. */ import { signBytes, verifyBytes } from "./sign.ts"; import type { SiteOwnershipCredentialSubject, ActorDelegationCredentialSubject, VCProof, } from "./schema.ts"; export type IdentityCredentialSubject = SiteOwnershipCredentialSubject | ActorDelegationCredentialSubject; /** * Produce canonical UTF-8 bytes from an identity credential subject. * DETERMINISM: identical inputs → identical bytes (sorted-key JSON, no whitespace). */ export function identityCredentialBytes(subject: IdentityCredentialSubject): Uint8Array { const canonical = JSON.stringify(subject, Object.keys(subject).sort()); return new TextEncoder().encode(canonical); } /** * Sign an identity credential subject and produce a W3C VC proof. * * @param subject — identity credential subject fields * @param privateKeyHex — 32-byte Ed25519 private key as hex (from env secret) * @param verificationMethod — DID key reference (e.g. "did:web:warpgogol.com#v1") * @param issuedAt — ISO-8601 timestamp for the proof creation date */ export async function signIdentityCredential( subject: IdentityCredentialSubject, privateKeyHex: string, verificationMethod: string, issuedAt: string, ): Promise { const proofValue = await signBytes(privateKeyHex, identityCredentialBytes(subject)); return { type: "Ed25519Signature2020", created: issuedAt, verificationMethod, proofPurpose: "assertionMethod", proofValue, }; } /** * Verify an identity credential proof against the published public key. * * @param subject — identity credential subject fields * @param proof — proof object from the credential * @param publicKeyMultibase — multibase public key from werkstatt.identity.json * @returns true if signature is valid */ export async function verifyIdentityCredential( subject: IdentityCredentialSubject, proof: VCProof, publicKeyMultibase: string, ): Promise { return verifyBytes(publicKeyMultibase, identityCredentialBytes(subject), proof.proofValue); }