Use case: CAdES sign in browser (detached, SHA-256) #6
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
EduarD3V/AutoFirme#6
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?
Use case: pick file + cert, produce .csig detached CAdES-BES signature, downloadable. Cross-verify: openssl cms -verify accepts output.
Reference code (source of truth for structure): decompiled/sources/es/gob/afirma/signers/cades/ — AOCAdESSigner, GenCAdESEPESSignedData, CAdESExtraParams. CMS via BouncyCastle there; browser side = WebCrypto sign (raw) + hand-built CMS/SignerInfo ASN.1 (DER encode in TS; consider @peculiar/asn1-cms or hand-rolled minimal encoder).
Acceptance:
Depends on #5.
UNBLOCK NOTICE: dependency policy relaxed — do not wait for #5 to merge. Work against this interface contract (web/src/lib/certs.ts, being finalized in #5):
Until #5 lands: mock LoadedCert in your branch (test RSA/EC key + self-signed cert generated at test time via openssl/node:crypto — commit no binaries). Test fixtures for golden vectors come from reference implementations (openssl for CAdES, xmlsec1 for XAdES) or generated by scripts at test setup. Resolve merge conflicts vs bullet-develop at integration; keep web/src/lib/certs.ts imports behind a thin adapter if the API shifts.
bullet: coder launched on branch
issue/6-use-case-cades-sign-in-browser(modelnan/deepseek-v4-flash).Claimed — branch
issue/6-use-case-cades-sign-in-browser. Doing: hand-rolled CMS/CAdES-BES signer (WebCrypto SHA-256 + DER encoder), wired to the #5 cert flow, .csig download, opensslcms -verifycross-check.Plan — Issue #6: Use case: CAdES sign in browser (detached, SHA-256)
Restated goal
Give the SPA a working CAdES-BES signer: the user picks a document and a signing certificate
(the #5 PKCS#12 flow), presses Firmar, and gets a
.csigfile — a detached CMSSignedDatawhose
SignerInfois signed with the certificate's private key via WebCrypto and carries theCAdES-BES signed attributes
contentType,messageDigest,signingTimeandsigningCertificateV2(SHA-256 inside a hand-rolled DER encoder; no new dependency)."Done" =
openssl cms -verify -inform DERaccepts our output against the fixture's CA (plus arefresh-with-
-cadesrun so the ESS attribute is checked), the negative case (tampered content)fails, a stored known-good CAdES vector generated by an independent implementation is compared
structurally, and all of it is committed with a green gate run.
Context found before planning
issue/6-…was branched fromorigin/main(6916892) which does not contain #5.#6 depends on #5 (
SignerCert.der,SignerCert.key,signBytes()), so task 0 fast-forwardsthe branch onto local
issue/5-use-case-load-signing-cert-fro(27d86a5, ×4 commits).web/src/lib/asn1.ts) — no writer. The writer is ours.decompiled/sources/es/gob/afirma/signers/cades/CAdESUtils.javaadds exactly:signingCertificateV2(getSigningCertificateV2, line 134-135) andsigningTime(line 171), on top of the CMS-standard
contentType+messageDigest.find / -name 'bcprov*.jar'→ empty)and the repo has no Java build for
decompiled/. A Java-generated golden is therefore notavailable without adding a dependency or a build; the golden vector is generated once with
openssl cms -sign -cadesinstead (independent implementation, same profile), and thatdeviation is recorded in the report.
openssl cms -sign -cades. Its output was inspectedwith
asn1parse:SignedData.version = 1,digestAlgorithm = sha256without NULL params,signerInfoattributescontentType,signingTime,messageDigest,signingCertificateV2(with
issuerSerial),signatureAlgorithm = rsaEncryption + NULL. Our encoder mirrors that.web/src/lib/x509.tskeeps only formatted strings for issuer/serial, socms.tshas no way toemit
IssuerAndSerialNumberorESSCertIDv2.issuerSerial. Two fields are added toCertificate.Scope
Will change (all under
web/):src/lib/asn1.tstlv/seq/setOf/oid/integer/…) next to the readersrc/lib/x509.tsissuerDer+serialDeronCertificate(needed by SignerInfo/ESS)src/lib/cms.tsSignedDatabuilder (the deliverable)src/lib/cms.test.tssrc/lib/sign.tsSelectedFilekeeps the sourceFile; smalldownloadByteshelpersrc/lib/App.svelte.csig→ download →ResultCardsrc/lib/App.test.tssrc/lib/components/SignButton.svelteonclicksrc/lib/i18n.tsREADME.mdsrc/test/fixtures.tssrc/test/fixtures/cades/*golden.csig)Will not touch:
decompiled/**,original/**,extracted/**,docs/**,.bullet/**,Containerfile,deploy/CI files,src/lib/pkcs12.ts/keystore.ts/pkcs12.test.ts,src/lib/components/CertPicker.svelte,FileDrop.svelte,FileCard.svelte,ResultCard.svelte.Ordered tasks (each with its verifying command)
issue/5-…so #5'ssign.ts/asn1.tsare present.→
git merge --ff-only issue/5-use-case-load-signing-cert-fro(done,27d86a5)asn1.ts(tlv,seq,setOfwith canonical sorting,oid,octetString,integerFromBytes,utcTime,explicit/implicitcontext tags).→
cd web && pnpm test -- asn1/pnpm checkx509.ts: addissuerDer/serialDertoCertificate.→
cd web && pnpm test -- pkcs12(existing suite must stay green)cms.ts:signCadesDetached(cert, content, { signingTime })— SHA-256 digest, four signedattributes,
SignerInfo, RSA (rsaEncryption) and ECDSA (raw → DER r||s) signatures.→
cd web && pnpm test -- cmscms.test.ts: RSA + EC sign →openssl cms -verify -inform DER -content … -CAfile ca.pem(
-purpose any) exit 0;openssl cms -verify -cadesexit 0; tampered content exits non-zero;attribute OIDs and
messageDigestasserted by parsing our own output.→
cd web && pnpm test -- cmsopenssl cms -sign -cades -md sha256(detached) intosrc/test/fixtures/cades/; test asserts openssl verifies the golden, and that our attributescover the golden's core OID set.
→
cd web && pnpm test -- cmsApp.svelte,SignButton.svelte,sign.ts,i18n.ts) and add the App test.→
cd web && pnpm testpnpm check+pnpm build, README note, commit, report.→
test -d decompiled;find decompiled -name '*.java' | grep -q .;git diff --checkOpen questions (assumption taken, no human to ask)
CertPickerhands out a singleSignerCert, so the.csigembeds onlythe signer certificate, not the CA. Assumed acceptable: the acceptance criterion is
openssl cms -verify, which takes-CAfileanyway. Noted in the report for a follow-up.openssl cms -sign -cadesoutputrather than the Java reference. Assumed acceptable (independent, same profile); recorded.
signingTime.new Date()at sign time; injectable so tests are deterministic.GeneralizedTime; out of scope.Risk
-cadesverification rejects oursigningCertificateV2encodingBufferSourcefriction (the #5Bytesalias exists for this)Bytes; cast at the WebCrypto boundary onlyx509.tsconflicts with #5's merged PRURL.createObjectURLfor the downloadvi.stubGlobal; the crypto evidence lives in the node testGate list (
.bullet/bullet.yaml) is unchanged and is not weakened anywhere.Refs #6in every commit; no closing keyword.DONE — branch
issue/6-use-case-cades-sign-in-browser(stacked on a fast-forward merge of #5's27d86a5).Commits:
68a674dDER writer + raw issuer/serial on parsed certs ·388e484CAdES-BES detached SHA-256 builder + openssl-verified tests + golden vector ·fab0842UI wiring (sign → .csig download). HEADfab0842.Gates:
test -d decompiled→ 0 ·find decompiled -name '*.java' | grep -q .→ 0 ·git diff --check→ 0 (plusgit ls-files -- original extracted work→ 0 paths).Verification:
pnpm test23/23 ·pnpm check0 errors ·pnpm buildok · real-Chromium smoke signed a document and producedcontrato.pdf.csig, whichopenssl cms -verify -inform DER … -binaryand-cadesboth accept; recovered content is byte-identical to the original; 0 external requests.Full detail, deviations (golden vector from openssl rather than Java/BC — no BC jar on this machine; signer cert only, no chain embedded) and the
-binarycanonicalisation trap for #7/#11:.bullet/meta/issue-6.report.md.bullet: coder session died — relaunched from committed state (branch
issue/6-use-case-cades-sign-in-browser).DONE (re-verified by the relaunched session) — branch
issue/6-use-case-cades-sign-in-browser, HEAD7e4b55e(stacked on the fast-forward of #5's27d86a5).Added since the previous DONE comment:
7e4b55eweb: stop labelling the implemented CAdES format as pending (Refs #6)— the format picker showedpendiente #6next to the working CAdES option;SignFormatOption.pendingnow drives the badge (andcanSign/hint/extension come from the selected format, not a hardcoded'cades').Re-verification from the committed tree:
pnpm test23/23 ·pnpm check0 errors ·pnpm buildok · gates (test -d decompiled,find decompiled -name '*.java' | grep -q .,git diff --check) all 0,git ls-files -- original extracted work0 paths, tree clean, branch not pushed.Real Chromium end-to-end: upload doc → PKCS#12 (PIN 1234) → Cargar certificados → Firmar →
contrato.txt.csig(1515 B) downloaded;openssl cms -verify -inform DER -content … -CAfile ca.pem -purpose any -binaryand-cadesboth exit 0, recovered content byte-identical. Golden fixture verified the same way outside the test harness.Full detail and deviations (golden generated with
openssl cms -sign -cades, no BC jar on this machine; signer cert only, no chain embedded; the-binarycanonicalisation trap for #7/#11):.bullet/meta/issue-6.report.md.bullet: coder session died — relaunched from committed state (branch
issue/6-use-case-cades-sign-in-browser).DONE (third session, re-verified — no code change) — branch
issue/6-use-case-cades-sign-in-browser, HEAD7e4b55e, stacked on the fast-forward of #5's27d86a5. Nothing pushed, worktree clean.Commits:
68a674dDER writer + raw issuer/serial onCertificate·388e484hand-built CAdES-BES detached SHA-256 + openssl cross-verification ·fab0842UI: sign the picked document and download the.csig·7e4b55estop labelling the implemented CAdES format as pending.Gates (
.bullet/bullet.yaml), all exit 0:test -d decompiled→ 0find decompiled -name '*.java' | grep -q .→ 0git diff --check→ 0Acceptance evidence, re-run first-hand this session from the committed tree:
cd web && pnpm test→ 23/23;pnpm check→ 375 files, 0 errors;pnpm build→ exit 0.openssl cms -verify -inform DER -in web/src/test/fixtures/cades/golden.csig -content content.txt -CAfile ca.pem -purpose any -binary -cades→CAdES Verification successful.pnpm preview --port 4180, Chromium): uploadcontrato.txt+ a newly generated CA-signedkeystore.p12(PIN 1234) → Cargar certificados → Firmar → cardFirma CAdES-BES creada. Se ha descargado el fichero .csig., downloadcontrato.txt.csig(1.5 KB,blob:, 14 requests all local, zero console/page errors).openssl cms -verify -inform DER … -CAfile ca.pem -purpose any -binary→CMS Verification successful, exit 0, recovered content byte-identical; with-cades→CAdES Verification successful;-cmsout -printshowscontentType,signingTime,messageDigest,id-smime-aa-signingCertificateV2.Deviations a human must decide (unchanged from the previous DONE comment):
openssl cms -sign -cades, not the Java reference — no BC jar exists anywhere on this machine anddecompiled/has no build..csigis not self-contained — verifier needs-CAfile.cms.tsthrows for any other hash binding.Full report:
.bullet/meta/issue-6.report.md.Refs #6.bullet: coder session died — relaunched from committed state (branch
issue/6-use-case-cades-sign-in-browser).DONE (fourth session, re-verified from the committed tree — no code change) — branch
issue/6-use-case-cades-sign-in-browser.Commits:
68a674dDER writer + raw issuer/serial;388e484hand-built CAdES-BES detached SHA-256;fab0842sign the picked document and download the .csig;7e4b55estop labelling the implemented CAdES format as pending. HEAD7e4b55e, tree clean, branch not pushed (Refs #6).Gates (
.bullet/bullet.yaml):test -d decompiled→ 0;find decompiled -name '*.java' | grep -q .→ 0 (11487 files);git diff --check→ 0; extragit ls-files -- original extracted work→ 0 paths.Evidence this session:
pnpm test23/23;pnpm check375 files 0 errors;pnpm buildexit 0; goldengolden.csigaccepted byopenssl cms -verify … -binaryand-cades.New harness-free end-to-end: fresh
opensslRSA CA+leaf →pkcs12, then the production modules (cms.ts+keystore.ts) run under Node 26 type-stripping (copiesdiff-identical to source modulo import specifiers) → 1386-byte ContentInfo;openssl cms -verify -inform DER -content … -CAfile ca.pem -purpose any -binary→CMS Verification successful,-cades→CAdES Verification successful, tampered content →CMS Verification failureexit 4. Attributes present:contentType,signingTime,messageDigest,id-smime-aa-signingCertificateV2.Deviations for a human (details in the report): (1) golden vector generated with
openssl cms -sign -cades, not the Java/BC reference — re-checked this session, there is no BouncyCastle jar anywhere on the machine; (2) only the signer certificate is embedded, not the CA chain, so a.csigneeds the issuer to verify; (3) SHA-256 only; (4)signingTimealways set.Operator note:
BULLET_TOKENhere authenticates asgotoi-agent, which 404s onEduarD3V/AutoFirme; this comment used the basic credentials inBULLET_BASIC.bullet: coder session died — relaunched from committed state (branch
issue/6-use-case-cades-sign-in-browser).bullet: implementation finished (DONE (fourth session, re-verified from the committed tree — no code change) — branch
issue/6-use-case-cades-sign-in-browser.) — moving to review.bullet: reviewer launched (model
9router/reviewer, independent of coder).DONE (fifth session, re-verified — no code change) — branch
issue/6-use-case-cades-sign-in-browser, HEAD7e4b55e.Commits:
68a674dDER writer + raw issuer/serial on certs ·388e484hand-built CAdES-BES detached SHA-256 ·fab0842sign + download wiring ·7e4b55eformat-picker copy fix.Gates:
test -d decompiled0 ·find decompiled -name '*.java' | grep -q .0 ·git diff --check0 ·git ls-files -- original extracted work0 paths.Suite
pnpm test23/23 ·pnpm check0 errors/0 warnings ·pnpm build0.Independent evidence this session (fresh CA/leaf/p12, nothing inherited): production modules run harness-free under Node 26 → 1370-byte
.csig,openssl cms -verify -inform DER -binaryand-cadesexit 0, recovered contentcmp-identical, tampered document exit 4; real headless-Chromium run (preview :4191, p12 PIN 1234) producedcontent.txt.csigvia the Firmar button, same openssl verification exit 0,contentType/signingTime/messageDigest/signingCertificateV2all present, console and page errors empty. Golden fixturesrc/test/fixtures/cades/golden.csigstill verifies standalone.Two deviations a human must decide (unchanged, detailed in the report): the golden vector was generated with
openssl cms -sign -cadesbecause no BouncyCastle jar exists anywhere on this machine (find / -xdev -name 'bc*.jar'andfind decompiled -name '*.jar'both empty); and only the signer certificate is embedded, not the CA chain.Worktree clean, no new commit. Refs #6.
REVIEW: CLEAN — commit
7e4b55e: findings none — CAdES-BES detached SHA-256 with signingTime/contentType/messageDigest/signingCertificateV2 (web/src/lib/cms.ts), openssl cms -verify + -cades green for RSA/EC/golden, negative tamper cases discriminate, .csig download wired and tested, 23/23 + pnpm check + build + gates reproduced by reviewer; deviations accepted: golden generated with openssl (no BC jar on machine), single-cert embedding — see meta/issue-6.review.mdbullet: review CLEAN — ready to publish.
bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: PR #17 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.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.Claimed — branch
bullet-develop. Doing: mergeorigin/mainintobullet-developfor the rolling PR; the live issue/6 integration session in the same worktree is left untouched.INTEGRATED: #6 merged into bullet-develop (conflicts resolved: kept integration
implementedformat flag + generic signDone copy, took #6SelectedFile.sourceand its hand-built CMS/ESS engine, one App.svelte sign() dispatching XAdES+CAdES, kept both UI tests via a shared download-capture helper and both fixture sets)RECONCILED: trunk work merged into bullet-develop (conflicts resolved: none —
origin/main's tree was already identical to the integration tree, so the reconciliation is the ancestry-only merge10e66a7; the concurrent issue/6 session's5834bc4sits on top of it).DONE — merge
10e66a7(parents41af16a+890e564); branch now5834bc4, 0 behindorigin/main. No push by this session.Gates:
test -d decompiled→ 0;find decompiled -name '*.java' | grep -q .→ 0 (11,487);git diff --check 41af16a 10e66a7→ 0.The 7 conflicted paths seen in this worktree belong to the live issue/6 integration session — left untouched.
bullet: merged into
bullet-develop— integration gates green.bullet: PR #17 closed — work already merged into
bullet-develop.