{"version":3,"sources":["../../src/crypto/jcs.ts","../../src/crypto/hash.ts","../../src/crypto/manifest.ts","../../src/crypto/merkle.ts"],"names":["sha256","createHash"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * JCS (RFC 8785) canonicalization.\n *\n * Deterministic JSON serialization. Without this, two servers might\n * produce different byte strings for the same object, breaking hash\n * equality and verification. JCS sorts object keys lexically and\n * uses stable number / string formatting.\n *\n * This implementation matches Python's\n *\n *   json.dumps(o, sort_keys=True, separators=(\",\", \":\"), ensure_ascii=False)\n *\n * for all JSON-safe values used in EnvelopeV1, STH, and bundle JSON\n * files. Cross-language parity is a hard invariant — any change here\n * requires a coordinated update to the Python SDK's equivalent\n * function and a re-run of the parity fixtures (see\n * `test/envelope-parity.spec.ts` and `test/sth-parity.spec.ts`).\n *\n * Functionally equivalent to `shared-deps/chain/lib/crypto.ts::jcs`.\n * Reimplemented here rather than imported because:\n *   - the SDK has zero runtime dependencies (shared-deps pulls in\n *     `@aws-sdk/client-kms` for backend signing);\n *   - duplicating ~25 lines is cheaper than a peer-dep treadmill;\n *   - the parity tests cross-validate byte-for-byte against\n *     shared-deps' fixture, so drift is caught at CI time.\n */\n\nexport function jcs(value: unknown): string {\n  if (value === null) return \"null\";\n  if (typeof value === \"number\") {\n    if (!Number.isFinite(value)) {\n      throw new Error(\"JCS: non-finite number\");\n    }\n    return value.toString();\n  }\n  if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n  if (typeof value === \"string\") return JSON.stringify(value);\n  if (Array.isArray(value)) {\n    return \"[\" + value.map(jcs).join(\",\") + \"]\";\n  }\n  if (typeof value === \"object\") {\n    const obj = value as Record<string, unknown>;\n    const keys = Object.keys(obj).sort();\n    return (\n      \"{\" +\n      keys.map((k) => JSON.stringify(k) + \":\" + jcs(obj[k])).join(\",\") +\n      \"}\"\n    );\n  }\n  throw new Error(\"JCS: unsupported type \" + typeof value);\n}\n","/**\n * Hash primitives — SHA-256 over Buffers and strings, plus the\n * canonical envelope-hash and STH-hash entry points.\n *\n * Uses `node:crypto` only. Browser / non-Node runtimes are not\n * supported in v1; if a Phase 2 use case needs them we can swap in\n * `globalThis.crypto.subtle` behind a thin abstraction.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { jcs } from \"./jcs.js\";\n\nexport function sha256(buf: Buffer | Uint8Array | string): Buffer {\n  const input =\n    typeof buf === \"string\"\n      ? buf\n      : Buffer.isBuffer(buf)\n        ? buf\n        : Buffer.from(buf);\n  return createHash(\"sha256\").update(input).digest();\n}\n\nexport function sha256Hex(buf: Buffer | Uint8Array | string): string {\n  return sha256(buf).toString(\"hex\");\n}\n\n/**\n * Canonical envelope hash.\n *\n * Per ARCHITECTURE.md §5, the envelope does not contain a signature.\n * Defensively strip any `sig` field before hashing — protects\n * against dynamic callers (e.g. negative-case tests, or a deserialized\n * pre-spec envelope) that might include one.\n *\n * The result is the value the issuer signs, the value emitted as\n * `envelope_hash` everywhere, and the value verifiers compare to the\n * `envelope.hash` file inside a bundle.\n */\nexport function envelopeHash(envelope: Record<string, unknown>): string {\n  const withoutSig: Record<string, unknown> = { ...envelope };\n  delete withoutSig.sig;\n  return sha256Hex(jcs(withoutSig));\n}\n\n/**\n * Canonical STH hash.\n *\n * Per ARCHITECTURE.md §6.3, the chain-master signature is stored in\n * the `master_sig` field on the wire form. Strip both `master_sig`\n * and the legacy `sig` (defensively, for any pre-M03 STHs that\n * might still be in flight) before hashing.\n *\n * After stripping, JCS sorts the remaining keys lexically — the\n * preimage byte order is `alg, checkpoint, key_id, root, segment,\n * timestamp, tree_size, v`.\n */\nexport function sthHash(sth: Record<string, unknown>): string {\n  const withoutSigs: Record<string, unknown> = { ...sth };\n  delete withoutSigs.master_sig;\n  delete withoutSigs.sig;\n  return sha256Hex(jcs(withoutSigs));\n}\n","/**\n * `MANIFEST.txt` serializer + parser.\n *\n * Format (per ARCHITECTURE.md §8 D.9):\n *\n *     <64 hex chars><two spaces><path><LF>\n *\n * Drop-in compatible with `sha256sum -c MANIFEST.txt` on Linux and\n * `shasum -a 256 -c MANIFEST.txt` on macOS. Lines are sorted\n * lexically by `path` so two builds of the same bundle produce\n * byte-identical MANIFEST contents.\n *\n * Hex digests are 64 lowercase characters (256 bits). No `0x`\n * prefix. Two literal ASCII spaces separate digest from path —\n * matching GNU coreutils output exactly. Trailing newline after\n * every entry.\n *\n * Paths are bundle-relative POSIX-style with forward slashes\n * (`timestamps/digicert.tsr`, NOT `timestamps\\\\digicert.tsr`). The\n * bundle's zip already enforces forward slashes; manifest paths\n * inherit.\n *\n * Functionally equivalent to\n * `shared-deps/chain/bundle/manifest.ts`. Reimplemented here for\n * dependency-zero parity reasons (see `./jcs.ts` rationale).\n */\n\nimport type { ManifestEntry } from \"../types/index.js\";\n\nconst SHA256_HEX_LEN = 64;\nconst SEPARATOR = \"  \"; // exactly two spaces — sha256sum convention\n\n/**\n * Serialize a list of manifest entries to `MANIFEST.txt` text.\n *\n * Sorts by `path` (lexical) before emitting so output is\n * deterministic — re-running on the same input always produces the\n * same bytes. Sorting is non-mutating; the input array is not\n * modified.\n *\n * Throws if any `sha256` field is not 64 lowercase hex characters,\n * or if any `path` contains a literal newline. Callers should\n * validate inputs upstream; this is defense-in-depth.\n */\nexport function serializeManifest(entries: readonly ManifestEntry[]): string {\n  const sorted = [...entries].sort((a, b) =>\n    a.path < b.path ? -1 : a.path > b.path ? 1 : 0,\n  );\n  const lines: string[] = [];\n  for (const e of sorted) {\n    if (!isValidSha256Hex(e.sha256)) {\n      throw new Error(\n        `manifest: invalid sha256 for \"${e.path}\": expected 64 lowercase hex chars`,\n      );\n    }\n    if (e.path.includes(\"\\n\") || e.path.includes(\"\\r\")) {\n      throw new Error(\n        `manifest: path contains newline: ${JSON.stringify(e.path)}`,\n      );\n    }\n    lines.push(e.sha256 + SEPARATOR + e.path + \"\\n\");\n  }\n  return lines.join(\"\");\n}\n\n/**\n * Parse `MANIFEST.txt` text back into entries.\n *\n * Strict — rejects:\n *   - lines that don't match the `<64 hex>  <path>` shape\n *   - duplicate paths (the bundle builder never produces duplicates;\n *     a duplicate at parse time means the manifest was tampered\n *     with or hand-edited)\n *   - paths beginning with whitespace (defends against an attacker\n *     padding the separator with extra spaces)\n *\n * Tolerates a trailing empty line (CRLF or LF). Does NOT tolerate\n * arbitrary whitespace around fields — the format is rigid by\n * design so malicious manifests can't sneak in lookalike paths via\n * normalization.\n *\n * Returns entries in their on-disk order. Callers that need a\n * canonical order should sort by `path`; `serializeManifest`\n * round-trips a sort.\n */\nexport function parseManifest(text: string): ManifestEntry[] {\n  const out: ManifestEntry[] = [];\n  const seen = new Set<string>();\n  const lines = text\n    .split(\"\\n\")\n    .map((l) => (l.endsWith(\"\\r\") ? l.slice(0, -1) : l));\n  while (lines.length > 0 && lines[lines.length - 1] === \"\") {\n    lines.pop();\n  }\n  for (let i = 0; i < lines.length; i++) {\n    const line = lines[i] as string;\n    if (line.length === 0) {\n      throw new Error(`manifest: empty line at line ${i + 1}`);\n    }\n    if (line.length < SHA256_HEX_LEN + SEPARATOR.length + 1) {\n      throw new Error(`manifest: short line at line ${i + 1}`);\n    }\n    const sha256 = line.slice(0, SHA256_HEX_LEN);\n    const sep = line.slice(SHA256_HEX_LEN, SHA256_HEX_LEN + SEPARATOR.length);\n    const path = line.slice(SHA256_HEX_LEN + SEPARATOR.length);\n    if (!isValidSha256Hex(sha256)) {\n      throw new Error(`manifest: invalid sha256 at line ${i + 1}`);\n    }\n    if (sep !== SEPARATOR) {\n      throw new Error(`manifest: missing two-space separator at line ${i + 1}`);\n    }\n    if (path.length === 0) {\n      throw new Error(`manifest: empty path at line ${i + 1}`);\n    }\n    if (path[0] === \" \" || path[0] === \"\\t\") {\n      throw new Error(`manifest: path begins with whitespace at line ${i + 1}`);\n    }\n    if (seen.has(path)) {\n      throw new Error(`manifest: duplicate path at line ${i + 1}: ${path}`);\n    }\n    seen.add(path);\n    out.push({ path, sha256 });\n  }\n  return out;\n}\n\n/**\n * True iff `s` is exactly 64 lowercase hex characters. The format\n * is deliberately strict — uppercase or short digests are rejected\n * so verifiers can rely on byte-equality of MANIFEST contents\n * across platforms.\n */\nexport function isValidSha256Hex(s: string): boolean {\n  if (s.length !== SHA256_HEX_LEN) return false;\n  for (let i = 0; i < SHA256_HEX_LEN; i++) {\n    const c = s.charCodeAt(i);\n    const isDigit = c >= 0x30 && c <= 0x39;\n    const isLowerHex = c >= 0x61 && c <= 0x66;\n    if (!isDigit && !isLowerHex) return false;\n  }\n  return true;\n}\n","/**\n * RFC 6962 Certificate-Transparency-style Merkle tree primitives.\n *\n * - Domain-separated leaf and node prefixes (0x00, 0x01) to prevent\n *   second-preimage / length-extension attacks.\n * - SHA-256 only in v1 (matches shared-deps default; SHA-512 lives\n *   on the backend tree but is irrelevant to v1 verification).\n * - Verification only: this SDK never builds trees, only walks\n *   audit paths. Tree construction lives in the backend\n *   (`anchor-service` Lambda) and is unnecessary here.\n *\n * Mirrors `shared-deps/chain/lib/merkle.ts::leafHash`,\n * `nodeHash`, `verifyInclusion`. Reimplemented for the same\n * dependency-zero reasons as `./jcs.ts`.\n */\n\nimport { createHash } from \"node:crypto\";\n\nconst LEAF_PREFIX = Buffer.from([0x00]);\nconst NODE_PREFIX = Buffer.from([0x01]);\n\nfunction h(...parts: Buffer[]): Buffer {\n  const d = createHash(\"sha256\");\n  for (const p of parts) d.update(p);\n  return d.digest();\n}\n\n/**\n * Hash a leaf payload (canonical envelope bytes).\n *\n * The result is the bundle's `merkle.json::leaf_hash` — NOT the\n * envelope hash. The two differ by the `0x00` prefix; a verifier\n * that compares them directly is wrong.\n */\nexport function leafHash(payload: Buffer | Uint8Array): Buffer {\n  const buf = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);\n  return h(LEAF_PREFIX, buf);\n}\n\n/** Hash an internal node from two children. */\nexport function nodeHash(left: Buffer, right: Buffer): Buffer {\n  return h(NODE_PREFIX, left, right);\n}\n\n/**\n * Verify an inclusion proof.\n *\n * Recomputes the root from `(leaf, m, n, path)` and compares against\n * `expectedRoot`. Consumes the path bottom-up (closest-to-leaf first),\n * matching the RFC 6962 §2.1.1 audit-path format and the order that\n * the backend's `inclusionProof` produces.\n *\n * Handles non-power-of-two sizes correctly: at levels where a subtree\n * is \"promoted\" without a sibling (`fn === sn`), the walk keeps\n * climbing until it encounters the next real sibling.\n */\nexport function verifyInclusion(\n  leaf: Buffer,\n  m: number,\n  n: number,\n  path: Buffer[],\n  expectedRoot: Buffer,\n): boolean {\n  if (m >= n || n === 0) return false;\n\n  let fn = m;\n  let sn = n - 1;\n  let r = leaf;\n\n  for (const sibling of path) {\n    if (sn === 0) return false; // over-long path\n    if ((fn & 1) === 1 || fn === sn) {\n      r = nodeHash(sibling, r);\n      while ((fn & 1) === 0 && fn !== 0) {\n        fn >>= 1;\n        sn >>= 1;\n      }\n    } else {\n      r = nodeHash(r, sibling);\n    }\n    fn >>= 1;\n    sn >>= 1;\n  }\n\n  return sn === 0 && r.equals(expectedRoot);\n}\n\n/**\n * Verify a consistency proof: old root (size m) → new root (size n).\n * RFC 6962 §2.1.4.\n *\n * Used by witness implementations and the consistency-check feature\n * in the Governance Console (M10). Verification only — no proof\n * construction in v1.\n */\nexport function verifyConsistency(\n  oldRoot: Buffer,\n  newRoot: Buffer,\n  m: number,\n  n: number,\n  proof: Buffer[],\n): boolean {\n  if (m < 0 || m > n) return false;\n  if (m === n) return proof.length === 0 && oldRoot.equals(newRoot);\n  if (m === 0) return proof.length === 0;\n\n  let fn = m - 1;\n  let sn = n - 1;\n  while ((fn & 1) === 1) {\n    fn >>= 1;\n    sn >>= 1;\n  }\n\n  let i = 0;\n  let oldHash: Buffer;\n  let newHash: Buffer;\n\n  if (fn === 0) {\n    oldHash = oldRoot;\n    newHash = oldRoot;\n  } else {\n    if (proof.length === 0) return false;\n    oldHash = proof[i] as Buffer;\n    newHash = proof[i] as Buffer;\n    i++;\n  }\n\n  while (sn !== 0) {\n    if (i >= proof.length) return false;\n    if ((fn & 1) === 1 || fn === sn) {\n      const sib = proof[i] as Buffer;\n      oldHash = nodeHash(sib, oldHash);\n      newHash = nodeHash(sib, newHash);\n      while ((fn & 1) === 0 && fn !== 0) {\n        fn >>= 1;\n        sn >>= 1;\n      }\n    } else {\n      const sib = proof[i] as Buffer;\n      newHash = nodeHash(newHash, sib);\n    }\n    i++;\n    fn >>= 1;\n    sn >>= 1;\n  }\n\n  return (\n    i === proof.length && oldHash.equals(oldRoot) && newHash.equals(newRoot)\n  );\n}\n\nexport const hex = (b: Buffer): string => b.toString(\"hex\");\nexport const unhex = (s: string): Buffer => Buffer.from(s, \"hex\");\n"]}