{"version":3,"file":"verifier.mjs","names":["deadline: number"],"sources":["../../src/zcap/verifier.ts"],"sourcesContent":["/**\n * ZCAP-LD verifier: walk chain + enforce caveats.\n *\n * A request is allowed only when:\n *   1. Chain anchors at a root the verifier trusts.\n *   2. Each link's proof verifies against its parent's controller.\n *   3. Each capability's `expires` + `allowedAction` are respected.\n *   4. Every caveat on every capability in the chain `check()`s OK.\n *   5. The invocation target matches at every level.\n */\n\nimport { CaveatViolation, caveatFromDict, parseIso8601 } from './caveats'\nimport { Capability, type InvocationContext } from './model'\nimport { ProofVerificationError, verifyProof } from './proofs'\n\nexport class ChainVerificationError extends Error {\n  public constructor(message: string) {\n    super(message)\n    this.name = 'ChainVerificationError'\n  }\n}\n\n/**\n * Allow-or-throw gate for a single invocation.\n *\n * @param chain - `[invocationCap, parent, ..., root]` ordered child-to-root.\n * @param context - Runtime request properties.\n * @param resolvePublicKey - `verificationMethod -> 32-byte Ed25519 pub`.\n * @param trustedRootController - DID that owns the resource; chain\n *   anchor's `controller` MUST equal this.\n */\nexport function verify({\n  chain,\n  context,\n  resolvePublicKey,\n  trustedRootController,\n}: {\n  chain: Capability[]\n  context: InvocationContext\n  resolvePublicKey: (verificationMethod: string) => Uint8Array\n  trustedRootController: string\n}): void {\n  if (chain.length === 0) {\n    throw new ChainVerificationError('empty capability chain')\n  }\n\n  const root = chain[chain.length - 1]\n  if (root.parentCapability) {\n    throw new ChainVerificationError(\n      `chain anchor ${root.id} has parentCapability ${JSON.stringify(root.parentCapability)}; expected root`\n    )\n  }\n  if (root.controller !== trustedRootController) {\n    throw new ChainVerificationError(\n      `chain anchors at controller ${JSON.stringify(root.controller)}, expected ${JSON.stringify(trustedRootController)}`\n    )\n  }\n\n  let previousController = trustedRootController\n  for (let idx = chain.length - 1; idx >= 0; idx--) {\n    const cap = chain[idx]\n\n    if (idx < chain.length - 1) {\n      const parent = chain[idx + 1]\n      if (cap.parentCapability !== parent.id) {\n        throw new ChainVerificationError(\n          `link mismatch: child.parentCapability=${JSON.stringify(cap.parentCapability)}, parent.id=${JSON.stringify(parent.id)}`\n        )\n      }\n    }\n\n    if (cap.invocationTarget !== root.invocationTarget) {\n      throw new ChainVerificationError(\n        `invocationTarget drift at ${cap.id}: ${JSON.stringify(cap.invocationTarget)} != root ${JSON.stringify(root.invocationTarget)}`\n      )\n    }\n\n    if (!cap.allowedAction.includes(context.verb)) {\n      throw new CaveatViolation(\n        `verb ${JSON.stringify(context.verb)} not in ${cap.id}'s allowedAction ${JSON.stringify(cap.allowedAction)}`\n      )\n    }\n\n    if (cap.expires) {\n      checkExpires(cap.expires, context.nowIso)\n    }\n\n    for (const raw of cap.caveat) {\n      const caveat = caveatFromDict(raw)\n      caveat.check(context)\n    }\n\n    if (!cap.proof) {\n      throw new ProofVerificationError(`cap ${cap.id} missing proof`)\n    }\n    const verifMethod = cap.proof.verificationMethod\n    if (!verifMethod) {\n      throw new ProofVerificationError(`cap ${cap.id} proof missing verificationMethod`)\n    }\n    // Parent's controller MUST own the key that signed the child.\n    if (\n      verifMethod !== previousController &&\n      !verifMethod.startsWith(previousController + '#')\n    ) {\n      throw new ProofVerificationError(\n        `cap ${cap.id} signed by ${JSON.stringify(verifMethod)} which is not controlled by expected ${JSON.stringify(previousController)}`\n      )\n    }\n\n    const publicKey = resolvePublicKey(verifMethod)\n    verifyProof({\n      capabilityDict: cap.toDict(),\n      proof: cap.proof,\n      publicKeyBytes: publicKey,\n    })\n\n    previousController = cap.controller\n  }\n}\n\nfunction checkExpires(expires: string, nowIso?: string): void {\n  let deadline: number\n  try {\n    deadline = parseIso8601(expires).getTime()\n  } catch (err) {\n    throw new ChainVerificationError(\n      `capability expires not ISO 8601: ${JSON.stringify(expires)}`\n    )\n  }\n  const now = nowIso ? parseIso8601(nowIso).getTime() : Date.now()\n  if (now > deadline) {\n    throw new CaveatViolation(`capability expired at ${expires} (now ${new Date(now).toISOString()})`)\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAa,yBAAb,cAA4C,MAAM;CAChD,AAAO,YAAY,SAAiB;AAClC,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;;;;;;AAahB,SAAgB,OAAO,EACrB,OACA,SACA,kBACA,yBAMO;AACP,KAAI,MAAM,WAAW,EACnB,OAAM,IAAI,uBAAuB,yBAAyB;CAG5D,MAAM,OAAO,MAAM,MAAM,SAAS;AAClC,KAAI,KAAK,iBACP,OAAM,IAAI,uBACR,gBAAgB,KAAK,GAAG,wBAAwB,KAAK,UAAU,KAAK,iBAAiB,CAAC,iBACvF;AAEH,KAAI,KAAK,eAAe,sBACtB,OAAM,IAAI,uBACR,+BAA+B,KAAK,UAAU,KAAK,WAAW,CAAC,aAAa,KAAK,UAAU,sBAAsB,GAClH;CAGH,IAAI,qBAAqB;AACzB,MAAK,IAAI,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO;EAChD,MAAM,MAAM,MAAM;AAElB,MAAI,MAAM,MAAM,SAAS,GAAG;GAC1B,MAAM,SAAS,MAAM,MAAM;AAC3B,OAAI,IAAI,qBAAqB,OAAO,GAClC,OAAM,IAAI,uBACR,yCAAyC,KAAK,UAAU,IAAI,iBAAiB,CAAC,cAAc,KAAK,UAAU,OAAO,GAAG,GACtH;;AAIL,MAAI,IAAI,qBAAqB,KAAK,iBAChC,OAAM,IAAI,uBACR,6BAA6B,IAAI,GAAG,IAAI,KAAK,UAAU,IAAI,iBAAiB,CAAC,WAAW,KAAK,UAAU,KAAK,iBAAiB,GAC9H;AAGH,MAAI,CAAC,IAAI,cAAc,SAAS,QAAQ,KAAK,CAC3C,OAAM,IAAI,gBACR,QAAQ,KAAK,UAAU,QAAQ,KAAK,CAAC,UAAU,IAAI,GAAG,mBAAmB,KAAK,UAAU,IAAI,cAAc,GAC3G;AAGH,MAAI,IAAI,QACN,cAAa,IAAI,SAAS,QAAQ,OAAO;AAG3C,OAAK,MAAM,OAAO,IAAI,OAEpB,CADe,eAAe,IAAI,CAC3B,MAAM,QAAQ;AAGvB,MAAI,CAAC,IAAI,MACP,OAAM,IAAI,uBAAuB,OAAO,IAAI,GAAG,gBAAgB;EAEjE,MAAM,cAAc,IAAI,MAAM;AAC9B,MAAI,CAAC,YACH,OAAM,IAAI,uBAAuB,OAAO,IAAI,GAAG,mCAAmC;AAGpF,MACE,gBAAgB,sBAChB,CAAC,YAAY,WAAW,qBAAqB,IAAI,CAEjD,OAAM,IAAI,uBACR,OAAO,IAAI,GAAG,aAAa,KAAK,UAAU,YAAY,CAAC,uCAAuC,KAAK,UAAU,mBAAmB,GACjI;EAGH,MAAM,YAAY,iBAAiB,YAAY;AAC/C,cAAY;GACV,gBAAgB,IAAI,QAAQ;GAC5B,OAAO,IAAI;GACX,gBAAgB;GACjB,CAAC;AAEF,uBAAqB,IAAI;;;AAI7B,SAAS,aAAa,SAAiB,QAAuB;CAC5D,IAAIA;AACJ,KAAI;AACF,aAAW,aAAa,QAAQ,CAAC,SAAS;UACnC,KAAK;AACZ,QAAM,IAAI,uBACR,oCAAoC,KAAK,UAAU,QAAQ,GAC5D;;CAEH,MAAM,MAAM,SAAS,aAAa,OAAO,CAAC,SAAS,GAAG,KAAK,KAAK;AAChE,KAAI,MAAM,SACR,OAAM,IAAI,gBAAgB,yBAAyB,QAAQ,QAAQ,IAAI,KAAK,IAAI,CAAC,aAAa,CAAC,GAAG"}