/* RFC-0715: TSA adapter for RFC 3161 timestamp queries — encodes TimeStampReq via pkijs/asn1js, sends HTTP POST, returns DER-encoded TimeStampResp. Does not verify the timestamp token — that is nachweis.verify-signature. Does not implement retry logic — caller handles transient failures. RFC-0715: initial TSA adapter with FreeTSA.org implementation. RFC-0715 review fix: use byteHash from @warpgogol/fingerprint (DNA-53). Replace FreeTsaAdapter class + createCustomTsaAdapter with HttpTsaAdapter class. RFC-1097: step 6 — compass.migrate codemod run Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order. RFC-1097: sweep — werkstatt-engine clean Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics. */ import { byteHash } from "@warpgogol/werkstatt-engine/fingerprint"; export interface TsaAdapter { readonly name: string; readonly url: string; timestamp(message: Uint8Array): Promise; } export class HttpTsaAdapter implements TsaAdapter { readonly name: string; readonly url: string; constructor(name: string, url: string) { this.name = name; this.url = url; } async timestamp(message: Uint8Array): Promise { const reqBytes = await encodeTimestampReq(message); const response = await fetch(this.url, { method: "POST", headers: { "Content-Type": "application/timestamp-query" }, body: Buffer.from(reqBytes), }); if (!response.ok) { throw new Error( `[${this.name}] TSA returned HTTP ${response.status}: ${response.statusText}`, ); } const respBuffer = await response.arrayBuffer(); return new Uint8Array(respBuffer); } } export const FreeTsaAdapter = new HttpTsaAdapter("FreeTSA", "https://freetsa.org/tsr"); export async function encodeTimestampReq(message: Uint8Array): Promise { const { default: pkijs } = await import("pkijs"); const { default: asn1js } = await import("asn1js"); const hashHex = byteHash(message).replace("sha256:", ""); const hashBytes = Buffer.from(hashHex, "hex"); const hashBuffer = hashBytes.buffer.slice( hashBytes.byteOffset, hashBytes.byteOffset + hashBytes.byteLength, ); const messageImprint = new pkijs.MessageImprint({ hashAlgorithm: new pkijs.AlgorithmIdentifier({ algorithmId: "2.16.840.1.101.3.4.2.1", }), hashedMessage: new asn1js.OctetString({ valueHex: hashBuffer }), }); const nonceBytes = new Uint8Array(10); crypto.getRandomValues(nonceBytes); const tspReq = new pkijs.TimeStampReq({ version: 1, messageImprint, reqPolicy: "1.2.3.4.5.6", certReq: true, nonce: new asn1js.Integer({ valueHex: nonceBytes.buffer }), }); const reqSchema = tspReq.toSchema(); const reqBer = reqSchema.toBER(false); return new Uint8Array(reqBer); }