E2E sign+verify: real valid FNMT cert (current-cert.p12) + generated self-signed p12 #39
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
EduarD3V/AutoFirme#39
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
Prove the full sign→verify loop with TWO keystores through the committed paths (UI flow + headless script):
work/certs/current-cert.p12(FNMT citizen cert, valid until 2028-12-31) — signs cleanly, verify reports valid.Secrets (HARD RULES)
work/certs/(old-cert.p12, current-cert.p12). PIN in gitignored.p12pass(same for both). NEVER print/echo/commit PIN or cert contents.Tasks
scripts/smoke-real.sh/ driver:--cert <path>arg; run for current-cert.p12 AND a generated fixture p12 (temp, never committed).Gates
pnpm test green, build clean, smoke exits 0 with verifiable signatures for both keystores.
Non-goals
No expired-cert logic (issue for expired handling is separate). No MCP, no PWA.
bullet: coder launched on branch
issue/39-e2e-sign-verify-real-valid-fnm(modelnan/deepseek-v4-flash).Claimed — branch
issue/39-e2e-sign-verify-real-valid-fnm. Doing: newscripts/smoke-real.shheadless E2E driver (load p12 → CAdES sign → verify, cross-checked with openssl) run against both the real FNMT keystore and a generated fixture, plus README UI-flow section.Issue #39 — E2E sign+verify with two keystores
Restated goal
Prove, through the app's own committed code paths, that a full loop load PKCS#12 → pick signer →
CAdES-sign a document → verify the produced
.csigworks for two keystores:work/certs/current-cert.p12(PIN in gitignored.p12pass), andsrc/test/fixtures.tshelper (temp, never committed)."Done" =
bash scripts/smoke-real.shexits 0 printing a verifiable signature for each keystore(with the generated one always, with the real one whenever its gitignored files exist — otherwise a
notice and exit 0),
pnpm testgreen,pnpm buildclean, and README documenting both the headlessrun and the equivalent UI flow (screenshot-able step list).
Scope
Files I will change (all additions except README/package.json):
scripts/smoke-real.sh— CLI wrapper:--cert <path>,--pin-file <path>,--generated/--no-generated. Missing cert/PIN file ⇒ notice +exit 0.scripts/smoke-real.test.ts— the driver itself: runs the loop forSMOKE_CERT(if set) and for the generated fixture; asserts signature validity, signer identity,validity dates; cross-checks with
openssl.scripts/vitest.smoke.config.ts— minimal vitest config so the driver is runnableoutside the app suite (root include is
src/**/*.test.ts, node environment, no jsdom).package.json— one line:"verify:real": "bash scripts/smoke-real.sh"(mirrorsthe existing
verify:zip).README.md— new section: headless run + the UI flow for both keystores.Explicitly not touched:
src/lib/**(no library change — the loop must run on today's code),test/**,src/test/**,Containerfile,Caddyfile,vite.config.ts, anything underoriginal/,work/,.p12pass,.gitignore. No new dependency. No PIN or cert bytes are everprinted, echoed or committed.
Ordered tasks
loadSignerSession+verifyArtifactreport, incl. thecertChaincheckbash scripts/smoke-real.sh --cert <abs>/work/certs/current-cert.p12 --pin-file <abs>/.p12pass(temp probe first)bash scripts/smoke-real.sh --generated⇒ exit 0,openssl cms -verifyOKbash scripts/smoke-real.sh --cert <abs>/work/certs/current-cert.p12 --pin-file <abs>/.p12pass⇒ exit 0bash scripts/smoke-real.sh --cert /tmp/does-not-exist.p12⇒ exit 0 + noticepnpm exec vitest rununaffected; re-read sectionpnpm test,pnpm build,pnpm check,git diff --checkCommit after each verified task:
<area>: <what> (Refs #39).Open questions → assumption taken
work/certs/+.p12passare gitignored and absent from this worktree (they live only in theoperator's checkout). Assumption: the wrapper defaults to repo-relative
work/certs/current-cert.p12and
.p12pass, skips with a notice when they are missing, and the real-keystore evidence for thisissue is produced by pointing
--cert/--pin-fileat those read-only absolute paths (nevercopied into the worktree, never printed). The committed script keeps working for any user who
drops a keystore in
work/certs/.signCadesDetachedembeds only the signer certificate, and the bundled rootshold the FNMT root. The self-signed fixture's leaf is signed by the fixture CA, which is not a
bundled root — so
certChainmay fail. Assumption: the driver assertsreport.okand suppliesthe container's own non-signer certificates as extra
trustedRoots(which is what the existingverify tests do with the throwaway CA). If the FNMT chain still cannot be built from the bundled
roots, the driver asserts every check except
certChainand prints thecertChainstatus andreason explicitly rather than hiding it.
flow is documented as a screenshot-able numbered step list in README, generated from the real
component names/labels in
src/lib/App.svelte+CertPicker.svelte.Risk and fallback
cannot complete and #39 would be blocked by #5's PBES2-only limit. Fallback: report the exact
rejection message, keep the fixture case green, and report PARTIAL with the reason.
pnpm check/svelte-checkpicks up the newscripts/*.ts(tsconfigincludecovers*) and complains about Node globals. Fallback: the driver uses onlynode:imports andprocess(both typed by the already-installed@types/node).pnpm test(rootincludeissrc/**/*.test.ts), so the smoke file must not be matched by the app suite. Fallback: keep thedriver under
scripts/(outsidesrc/) and confirm with a run ofpnpm test --reporter=basic.: did not load at all — — because it is written the way the Windows/CryptoAPI exporter writes PKCS#12: BER indefinite lengths and chunked OCTET STRINGs (incl. the implicit of ). So + gained BER support, and gained (the smokes identity assertion caught the picker and the report printing different subjects).
Gates:
bash scripts/smoke-real.sh→ exit 0,2 passed. Real keystore (--cert <operator checkout>/work/certs/current-cert.p12 --pin-file <operator checkout>/.p12pass, read-only paths, never copied, PIN never printed):valid 2026-02-02T19:18:28.000Z → 2028-12-31T22:59:59.000Z,openssl cms -verify → CMS Verification successful. Generated fixture: same loop, same oracle result.no keystore):exit 0+ notice.pnpm buildclean,pnpm check0 errors/0 warnings,git diff --checkclean.pnpm test: red, pre-existing. 6 failed/135 passed — allxmlsec1 printed no verdict (exit 0): Verification status: OK(installed xmlsec1 wording vs the helper insrc/test/fixtures.ts) plus jsdom timing flakes inApp.test.ts. Baseline with my three source files reverted: 5 failed, same cause, and threeApp.test.tsruns gave three different failing sets. Nothing deleted/skipped/weakened.Two decisions for a human: (1) verifying your own fresh
.csigstill shows a red Trust chain card — signing embeds only the signer certificate (#6), soreport.ok=falseeven for the real FNMT keystore whose container carriesAC FNMT Usuarios; the smoke prints it and asserts the signature-level checks. (2) #40 proposesold-cert.p12as this scripts default, but that keystore is expired so fails by design — the two issues need reconciling.EOF
)
DONE — branch
issue/39-e2e-sign-verify-real-valid-fnm, commitsd9eaa0e1(BER PKCS#12),426dbab3(E2E smoke + README). Not pushed; report at.bullet/meta/issue-39.report.md.Delivered:
scripts/smoke-real.sh(--cert/--pin-file/--no-generated) +scripts/smoke-real.test.tsdriving the app's own modules through load → CAdES-sign → verify, with twoopenssloracles;pnpm verify:real; README section for the headless run and the matching UI flow; CI regression test for the real-world BER container shape.Scope amendment (needed by this issue's own acceptance):
work/certs/current-cert.p12did not load at all —not-pkcs12: unsupported DER length— because it is written the way the Windows/CryptoAPI exporter writes PKCS#12: BER indefinite lengths and chunked OCTET STRINGs, including the implicit[0]ofencryptedContent. Sosrc/lib/asn1.ts+src/lib/pkcs12.tsgained BER support, andsrc/lib/verify/x509.tsgained2.5.4.42 -> GN(the smoke's identity assertion caught the picker and the report printing different subjects for the same certificate).Gates:
bash scripts/smoke-real.sh— exit 0, 2 passed. Real keystore (--cert <operator checkout>/work/certs/current-cert.p12 --pin-file <operator checkout>/.p12pass, read-only paths, never copied, PIN never printed):valid 2026-02-02T19:18:28.000Z -> 2028-12-31T22:59:59.000Z,openssl cms -verify -> CMS Verification successful. Generated fixture: same loop, same oracle result.pnpm buildclean,pnpm check0 errors / 0 warnings,git diff --checkclean.pnpm test: red, pre-existing. 6 failed / 135 passed — allxmlsec1 printed no verdict (exit 0): Verification status: OK(installed xmlsec1 wording vs the helper insrc/test/fixtures.ts) plus jsdom timing flakes inApp.test.ts. Baseline with my three source files reverted: 5 failed, same cause, and threeApp.test.tsruns gave three different failing sets. Nothing deleted, skipped or weakened.Two decisions for a human: (1) verifying your own fresh
.csigstill shows a red Trust chain card — signing embeds only the signer certificate (#6), soreport.ok=falseeven for the real FNMT keystore whose container carriesAC FNMT Usuarios; the smoke prints that honestly and asserts the signature-level checks. (2) #40 proposesold-cert.p12as this script's default, but that keystore is expired socertValidityfails by design — the two issues need reconciling.bullet: coder session died — relaunched from committed state (branch
issue/39-e2e-sign-verify-real-valid-fnm).Claimed — branch
issue/39-e2e-sign-verify-real-valid-fnm. Doing: verify the smoke + plan already on the branch (realcurrent-cert.p12+ generated fixture), re-run every gate, fix what is actually red, then report.DONE — branch
issue/39-e2e-sign-verify-real-valid-fnm, commitsd9eaa0e1,426dbab3,6b32e900,1e44aba0,1bee11ac,93bdb5d8. Working tree clean, not pushed.Gates (all on the final commit):
pnpm test— PASS:Tests 141 passed (141), 16 files, exit 0pnpm build— PASS:Wrote site to "build", exit 0pnpm check— PASS:1324 FILES 0 ERRORS 0 WARNINGSpnpm verify:realwith the real keystore (read-only absolute--cert/--pin-file) — PASS, exit 0:Tests 2 passed (2); realCN=GARCIA APARICIO EDUARDO - 05718264G … C=ES, valid2026-02-02 → 2028-12-31,openssl cms -verify → CMS Verification successful; generatedCN=Ñandú Prueba …, same verdictpnpm verify:realdefault paths (keystore absent in the worktree) — PASS, exit 0, skip noticegit diff --check— cleanSince the previous session's report: fixed the two causes of its red
pnpm test—xmlsec1Verifynow reads xmlsec1 1.3.12'sVerification status: OK/FAILED(5 tests failed on the oracle's output;FAILEDstill maps toFAIL), and the jsdom WebCrypto waits got one sharedASYNC = { timeout: 15_000 }instead of testing-library's 1 s default (reproduced with this branch'ssrc/libreverted ⇒ predates #39; no assertion changed, nothing deleted or skipped). README UI steps now carry the Spanish labels the screenshots will show.Left for a human: (1) the two gate fixes sit outside #39's literal scope — separable with one revert; (2) verifying your own fresh
.csigshows a red Trust chain card because only the signer certificate is embedded (#6); (3)--cert work/certs/old-cert.p12failscertValidityby design (#40). Full report:.bullet/meta/issue-39.report.md.bullet: coder session died — relaunched from committed state (branch
issue/39-e2e-sign-verify-real-valid-fnm).DONE — re-verification session (session 3; sessions 1–2 died after committing and commenting). No new code: branch state is already the deliverable, every gate re-run and green.
Branch
issue/39-e2e-sign-verify-real-valid-fnm, 6 commits, tree clean, not pushed:d9eaa0e1(BER PKCS#12) ·426dbab3(E2E smoke + README) ·6b32e900(xmlsec1 1.3.12 verdict) ·1e44aba0·1bee11ac(jsdom crypto waits) ·93bdb5d8(README labels).Gates (this session, on
93bdb5d8):pnpm test— PASSTest Files 16 passed (16),Tests 141 passed (141), exit 0.pnpm build— PASSWrote site to "build", exit 0.bash scripts/smoke-real.sh --cert <op-checkout>/work/certs/current-cert.p12 --pin-file <op-checkout>/.p12pass— PASS exit 0,Tests 2 passed (2). Real:CN=GARCIA APARICIO EDUARDO - 05718264G …, serial0f51b50b…, valid2026-02-02T19:18:28.000Z → 2028-12-31T22:59:59.000Z,openssl cms -verify → CMS Verification successful. Generated:CN=Ñandú Prueba,2026-10-15 → 2027-10-15, same openssl verdict.bash scripts/smoke-real.sh(keystore absent from this worktree) — PASS exit 0, skip notice + generated case runs.git diff --check— clean.Secrets: keystore read read-only via absolute
--cert/--pin-file, nothing copied into the worktree; PIN scan of the tracked tree clean; no.p12committed.Known, reported as facts (unchanged from the previous report):
certChain: failon both keystores because signing embeds only the signer certificate (#6) —issuer certificate not included(FNMT) andself-signed root not in trust store(fixture), soreport.ok=false. UI evidence is the README section only (issue asks for manual-evidence).--cert work/certs/old-cert.p12failscertValidityby design — #40.bullet: coder session died — relaunched from committed state (branch
issue/39-e2e-sign-verify-real-valid-fnm).bullet: implementation finished (DONE: — re-verification session (session 3; sessions 1–2 died after committing and commenting). No new code: branch state is already the deliverable, every gate re-run and green.) — moving to review.
Claimed — branch
issue/39-e2e-sign-verify-real-valid-fnm. Doing: session 4 re-verification — re-ranpnpm test/pnpm build/scripts/smoke-real.sh(both keystores, exit 0) and walked the UI path in a real Chromium tab to check the manual-evidence steps; one doc fix committed (e2800d72) because the README named controls the app does not show.DONE — branch
issue/39-e2e-sign-verify-real-valid-fnm, tip commite2800d72(this session's only commit: README manual-evidence steps now name the controls the app really shows — no Certificado label, card is Seleccionar firma, file picker is Agregar firma, no algorithm string in the signed row). Sessions 1–3's commits stand:d9eaa0e1,426dbab3,6b32e900,1e44aba0,1bee11ac,93bdb5d8. Not pushed.Gates:
pnpm testexit 0 (16 files / 141 tests) ·pnpm buildexit 0 (static, Wrote site to "build") ·bash scripts/smoke-real.sh --cert <op>/work/certs/current-cert.p12 --pin-file <op>/.p12passexit 0, 2/2 pass,openssl cms -verifysuccessful on both keystores (real: valid 2026-02-02 → 2028-12-31) · missing-keystore skip path exit 0 ·git diff --checkexit 0.UI path walked in a real Chromium tab (generated fixture p12, temp, deleted): load → sign →
.csig(1520 B) verified by openssl → verify panel shows the seven checks, content digest passes once the document is attached. Store-save round-trip not exercised; README no longer claims more than was observed.Human call:
certChain: failon both keystores is expected (#6 embeds only the signer cert), so the smoke asserts the six signature checks and prints the chain as it comes — chain embedding is a separate change.bullet: reviewer launched (model
9router/reviewer, independent of coder).REVIEW: FIXED 1 — commit
e2800d72: findings: src/lib/asn1.ts indefinite-length BER flattening matched chunked strings by tag number only, so a context tag whose number collides (certBag/safeContents [3]/[4], GeneralName-style tags) was rewritten into a chunked primitive and lost its children — fixed in797fbe6c; UNFIXED: nonebullet: fixer round done — fresh reviewer re-checking.
REVIEW: FIXED 3 — commit
76c57d79: findings: (1) plan Scope promised a--generatedflag on scripts/smoke-real.sh that was missing — added, commit76c57d79; (2) the smoke driver leaked the generated fixture temp dir (325 autofirme-p12-* dirs accumulated) — cleaned in afterAll, commit76c57d79; (3) README UI step for the generated fixture gave no way to materialize modern.p12 and misdescribed the expired flag as amber (it is text-destructive red) — one-liner + wording fixed, commit76c57d79; UNFIXED: report gate row says "2/2 test files pass" but the run is 1 file / 2 tests (cosmetic wording in the implementer report, evidence block itself is correct)bullet: fixer launched (round 1/2).
FIXED: applied 4 —
76c57d79wire --generated (plan flag pair) + regression4c7e7e9cscripts/smoke-generated-flag.test.ts (stub-pnpm drives the wrapper: --generated reaches the runner with SMOKE_GENERATED=1, --no-generated keeps it off, last flag wins; pre-fix wrapper exits 2 on --generated);76c57d79README fixture step + "amber"→"red" + regression53c54ea9scripts/readme-fixture-command.test.ts (extracts the documented node one-liner from README.md itself, runs it in a private TMPDIR, asserts the printed modern.p12 exists; CertPicker.svelte:283 uses text-destructive = red);76c57d79fixture-dir cleanup + regression889aec9fscripts/smoke-fixture-leak.test.ts (nested driver run, $TMPDIR autofirme-p12-* count identical before/after: 337→337; pre-fix has no afterAll); MINOR report gate wording ("2/2 test files" → 1 file / 2 tests driver, 4 files / 7 tests with the new regressions) corrected in the fixer report — the report is the deliverable, no repo commit applies. Gates re-run: pnpm test 16 files/142 tests exit 0; pnpm check 0 errors; pnpm build exit 0; smoke-real.sh (both keystores) exit 0, 4 files/7 tests, openssl cms -verify successful on FNMT + generated; skip paths exit 0; git diff --check clean; no secrets tracked. Nothing weakened, no new deps.bullet: reviewer launched (model
9router/reviewer, independent of coder).REVIEW: FIXED 1 — commit
e7610b7d: findings: scripts/smoke-fixture-leak.test.ts snapshots the $TMPDIR autofirme-p12-* count while scripts/smoke-real.test.ts creates/removes the same prefix in a parallel sibling file (vitest default fileParallelism) — latent flake; fixed serially ine7610b7dand re-verified (5 straight full-smoke passes, 4 files / 7 tests); UNFIXED: nonebullet: fixer launched (round 2/2).
FIXED: applied 0 — round-2 finding 9 (leak-count race: scripts/smoke-fixture-leak.test.ts counts $TMPDIR autofirme-p12-* while scripts/smoke-real.test.ts creates/removes the same prefix under vitest fileParallelism) was already fixed by reviewer commit
e7610b7d(fileParallelism: false, scripts/vitest.smoke.config.ts:20), which is the branch head — verified here, no new commits needed: full smoke with the real FNMT keystore (--cert work/certs/current-cert.p12 --pin-file .p12pass) exit 0 twice + 3 further default smoke runs exit 0 — 5 consecutive 4-file/7-test passes, zero flake; pnpm test exit 0 — 16 files, 142 tests; pnpm check exit 0 — 0 errors/0 warnings; git status clean, git diff --check clean, git ls-files '*.p12' '.p12pass' → none tracked. UNFIXED: nonebullet: fixer round done — fresh reviewer re-checking.
REVIEW: CLEAN — commit
e7610b7d: findings nonebullet: review CLEAN — ready to publish.
bullet: branch
issue/39-e2e-sign-verify-real-valid-fnmnot on origin — cannot integrate.bullet: PR #54 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: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.Claimed — branch
issue/39-e2e-sign-verify-real-valid-fnm. Doing: re-apply #39 on top of bullet-develop and reconcile conflicts.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merged into
bullet-develop— integration gates green.bullet: PR #54 closed — work already merged into
bullet-develop.