RFC3161 timestamp (TSA) option for PAdES/CAdES #43
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
EduarD3V/AutoFirme#43
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
AutoFirma supports trusted timestamps. Add optional RFC3161 timestamping.
Scope (OWNERSHIP: timestamp module + one SignPanel option)
Gates
pnpm test green, build clean.
Non-goals
No new infra. Proxy endpoint is a doc note unless trivial.
bullet: coder launched on branch
issue/43-rfc3161-timestamp-tsa-option-f(modelnan/deepseek-v4-flash).Claimed — branch
issue/43-rfc3161-timestamp-tsa-option-f. Doing: RFC 3161 TSA client (src/lib/timestamp.ts), optionaltimeStampembedding in the PAdES/CAdES unsigned attributes, TSA URL field in the sign panel, mocked-TSA tests.Plan — #43 RFC3161 timestamp (TSA) option for PAdES/CAdES
Goal (restated)
Add an optional RFC 3161 timestamp: when the user gives a TSA URL, the app asks that TSA to
timestamp the CAdES signature value and embeds the returned token as the CMS
id-aa-timeStampTokenunsigned attribute — that is exactly what turns CAdES-BES/PAdES-B-B intoCAdES-T/PAdES-B-T. Default is off, so nothing changes for existing signers.
"Done" =
pnpm testandpnpm buildgreen, with tests that (a) never touch the network (fetch isstubbed), (b) run against a real openssl-generated timestamp reply fixture, and (c) prove the
token ends up inside the produced PDF/CMS and that openssl can validate it. The signing key still
never leaves the browser; the TSA sees only a SHA-256 digest.
Scope
New
src/lib/timestamp.ts— RFC 3161 client: buildTimeStampReqDER, POST it, validatePKIStatusInfo+ the token'smessageImprint, return the token DER.src/lib/timestamp.test.ts— mockedfetch; real openssltsreply as the oracle.Edited
src/lib/pades.ts—PadesSignatureOptions.timeStamp?: string; size/Contentsfor a token,request it over the signature value, put it in
SignerInfo.unsignedAttrs.src/lib/cms.ts—CadesSignOptions.timeStamp?: string, same embedding for the detached CAdES.src/lib/pades.test.ts— one B-T case: unsigned attr present, token parsed back, openssl checks.src/lib/App.svelte— TSA URL input in the "2. Configure the signature" card (this is the"SignPanel option"; the sign panel is that card, there is no
SignPanel.svelte).src/lib/i18n.ts— ES/EN copy for that input.test/fixtures/generate.sh,test/fixtures/README.md— TSA test certificate + committedthrowaway TSA key (same policy as
pades-signer.key, which is already tracked).README.md— feature line + the CORS/proxy note the issue asks for (deployment doc only).Not touched (deliberately)
src/lib/verify/**— validating existing timestamps in the verification report is a differentissue; out of scope here.
src/lib/xades.ts(XAdES has no unsigned-attribute slot),src/lib/batch.ts,src/lib/keystore.ts,Containerfile,Caddyfile— no new infra, no proxy endpoint implemented.src/lib/der.tswriter/reader +fetch.Ordered tasks (each with its verify command)
non-2xx and rejection handling. Tests stub
globalThis.fetch; the reply is produced byopenssl ts -replyfrom the generated query.Verify:
pnpm vitest run src/lib/timestamp.test.tstimeStampoption) +/Contentssizing headroom.Verify:
pnpm vitest run src/lib/pades.test.ts(new B-T case parses the CMS back out of thePDF and runs
openssl ts -verifyon the token).timeStampoption).Verify:
pnpm vitest run src/lib/cms.test.ts src/lib/timestamp.test.tsApp.svelte,i18n.ts).Verify:
pnpm vitest run src/lib/App.test.tsgit diff --checkpnpm test,pnpm build,pnpm check,git diff --check.Open questions (assumptions I proceed on, cannot ask)
SignPanel.svelte; the sign panel is the secondCardinsrc/lib/App.svelte. The TSA URL input goes there.client side, and document the optional same-origin proxy path (
/tsa→ rewrite) in the READMEinstead of shipping a server component — the issue explicitly allows this.
encryptedDigest(the CMSSignerInfo.signatureOCTET STRING content, ECDSA DER-encoded). Not the document digest.src/lib/der.ts(already used bypades.ts); do not add a thirdDER helper.
/Contentsand the probe CMS are sized with an 8 KiB token allowance whentimestamping is on; the real token is zero-padded to that slot, as the existing signature already
is. A token bigger than the allowance raises the existing "does not fit" error.
TSTInfo.messageImprintmust equalour digest, which is the check that actually matters.
implemented: falseinSIGN_FORMATS, so the UI option onlyreaches CAdES today; the PAdES option is exercised by tests.
Risk and fallback
ts -reply(config section required on 3.x). The config fileis written by
generate.shintotest/fixtures/scratch/(gitignored) and by the test into a tempdir. Fallback if
openssl tsmisbehaves in CI: keep the client tests (request DER + parsing) witha committed static reply and drop the live
ts -replystep; note it in the report./Contentsheadroom too small for a real ACCV token → the existing "does not fit" error.Fallback: raise the constant.
timeStampoption (absent⇒ byte-identical output).
Blocked (pre-existing gate, not this branch):
pnpm test→ 5 failed | 144 passed.Failing:
src/lib/xades.test.ts×4 +src/lib/App.test.ts > signs an XML document as XAdES-EPES ….First error line:
Error: xmlsec1 printed no verdict (exit 0): Verification status: OK.Cause: xmlsec1 1.3.12 here prints
Verification status: OK|FAILED;xmlsec1Verify()insrc/test/fixtures.tsaccepts onlyOK/FAILor astatus:prefix, so it throws on a successful verification.Baseline:
git stash+ same command on the parent commit → identical failures (xades.test.ts 4 failed | 6 passed). Not weakening or patching that oracle; continuing with the #43 work.DONE — commits
9cbeb6c9,6d499d84,16b28892,cda95f5c(branchissue/43-rfc3161-timestamp-tsa-option-f, nothing pushed).pnpm test— RED: 5 failed | 144 passed, all 5 pre-existing xmlsec1-1.3.12 wording failures (xades.test.ts×4 + App XAdES), identical on the parent commit; every new timestamp test passes.pnpm check— exit 0 (1326 files, 0 errors).pnpm build— exit 0.git diff --check— clean.Shipped:
src/lib/timestamp.ts(RFC 3161 client: TimeStampReq, PKIStatus + messageImprint validation, token attribute),timeStampoption insignPdf/signCadesDetached(CAdES-T / PAdES B-T), TSA address field in the sign panel (off by default), README proxy note. Tests use a stubbed fetch plus a throwawayopenssl tsTSA and verify both directions withopenssl ts -query/-verify; no network, no committed TSA key, no new dependency.bullet: coder session died — relaunched from committed state (branch
issue/43-rfc3161-timestamp-tsa-option-f).bullet: implementation finished (DONE — commits
9cbeb6c9,6d499d84,16b28892,cda95f5c(branchissue/43-rfc3161-timestamp-tsa-option-f, nothing pushed).) — moving to review.bullet: reviewer launched (model
9router/reviewer, independent of coder).DONE — re-verified relaunch of the coder session (first one died after committing; no new work was needed beyond a cosmetic test fix).
Commits (Refs #43):
9cbeb6c9,6d499d84,16b28892,cda95f5c,e948f7fa— branchissue/43-rfc3161-timestamp-tsa-option-f, tree clean, nothing pushed.Gates, re-run in this session:
git diff --check— exit 0, clean.pnpm check— exit 0 (1326 FILES 0 ERRORS 0 WARNINGS 0 FILES_WITH_PROBLEMS).pnpm build— exit 0 (Wrote site to "build").pnpm vitest run src/lib/timestamp.test.ts src/lib/pades.test.ts src/lib/cms.test.ts— exit 0, 21/21 passed (the tests added by this branch).pnpm test— RED, pre-existing and not this branch: (a)xmlsec1 1.3.12printsVerification status: OK, whilexmlsec1Verify()on the merge base (4c9f6c31, onbullet-develop) matches onlystatus: OK→ throws on success (4 × xades + 1 × App); byte-identical helper, issue #39 hit the same; (b) jsdomwaitFortiming flakes — the same App test passes alone (-t "keeps a signature picked before any document exists"→ 1 passed) and fails only inside the full run.Full detail in
.bullet/meta/issue-43.report.md.REVIEW: FIXED 2 — commit
82bb9806: findings: (1) xmlsec1 1.3.12 oracle regex missed the tool'sVerification status: OKline → 5 deterministicpnpm testfailures, pre-existing on parent — fixeda4ca1063; (2) CertPicker label-savewaitForhit the 1s default under full-suite load, failed 3/4 runs — fixed82bb9806(explicit 15s allowance, assertion unchanged, same pattern already in that test); UNFIXED: nonebullet: fixer launched (round 1/2).
bullet: fixer round done — fresh reviewer re-checking.
REVIEW: CLEAN — commit
27e83711: findings none; earlier findings all fixed —a4ca1063xmlsec1 1.3.x verdict parse in the oracle,82bb9806explicit wait allowance in CertPicker (assertion unchanged),2fbc5f5dsuite-wide asyncUtilTimeout=15s on the dom instance queries bind to (+ pinned by src/test/setup.test.ts),27e83711src/test/oracle.test.ts pins the 1.2.x/1.3.x verdict parse; UNFIXED: none. Gates at27e83711: pnpm test 19 files / 153 tests pass, pnpm build exit 0, pnpm check 0 errors, git diff --check clean. No test skipped or weakened (removed test lines = imports + timeout-only rewrap). No network in tests (fetch stubbed; oracle = local openssl ts). Refs #43bullet: review CLEAN — ready to publish.
bullet: PR #53 open →
bullet-develop(auto-merge follows).bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: PR #53 closed — work already merged into
bullet-develop.bullet: merged into
bullet-develop— integration gates green.