Skip to content

FURS response-JWS verification (authenticate FURS's EOR response)#5

Merged
terrxo merged 1 commit into
mainfrom
p2x-furs-response-verify
May 25, 2026
Merged

FURS response-JWS verification (authenticate FURS's EOR response)#5
terrxo merged 1 commit into
mainfrom
p2x-furs-response-verify

Conversation

@terrxo

@terrxo terrxo commented May 25, 2026

Copy link
Copy Markdown
Contributor

The final FURS engine slice: verify that FURS's response is authentically FURS-signed, not just sign our request.

What this adds

  • verifyFursResponse(token, fursCertPem) — verifies a FURS response's compact JWS (RS256) against FURS's public certificate (X509Certificate(certPem).publicKeycreateVerify), checks alg === RS256, and returns the decoded payload. Throws on a bad/missing signature or malformed token.
  • FursResponseSignatureError (502) — raised when a response fails verification (potential spoof/MITM); the EOR is not trusted.
  • Client wiringmakeFursClient({ …, fursResponseCertPem }): when set, every signed response is verified before the EOR is read. Omitted → decoded unverified (prior behaviour). Recommended on for production.

Why

The reference FURS clients (node-furs, python-furs, jurgenwerk) all skip response-signature verification (decode-only). That leaves the EOR spoofable. This closes it, making the client safe for real use.

Verification

bun test44 pass / 1 skip / 0 fail, tsc --noEmit clean. New tests cover:

  • a genuinely FURS-signed response verifies and returns the payload;
  • a tampered payload (forged EOR, same header+sig) is rejected;
  • a response signed by a different (non-FURS) key is rejected;
  • malformed / non-RS256 tokens are rejected.

No live round-trip needed — tests mint a "FURS-signed" response with an ephemeral key and verify against its cert.

Notes

  • For the test env, FURS's response-signing cert is published with the reference clients (test-sign.pem); production has its own. The engine takes it as config (fursResponseCertPem) rather than bundling a gov cert.
  • Branched off main 4afba33 (P2 merged). With this, the fiscalize engine is complete: e-invoice (e-SLOG/UBL, XSD-validated) + FURS (ZOI/EOR, request-signed + response-verified). P3 (Medusa integration) is the plugin track; P4 (service/MCP) later. The bun-mTLS bun.md line + the one-shot live-EOR confirm remain tracked for a Node-mTLS runtime.

🤖 Generated with Claude Code

verifyFursResponse(token, fursCertPem) verifies FURS's response JWS (RS256)
against FURS's public certificate (X509Certificate → public key) before the EOR
is trusted — guards against spoofed/tampered responses. The client enforces it
when `fursResponseCertPem` is configured, raising FursResponseSignatureError on
failure. Completes the FURS client for safe real use.

Tests: 44 pass / 1 skip. Response verify accepts a genuine FURS-signed response
and rejects tampered payloads, wrong-key signatures, and malformed tokens. Build
remains unit-only; mints test responses with an ephemeral key (no live needed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@terrxo terrxo merged commit 4a11715 into main May 25, 2026
2 checks passed
@terrxo terrxo deleted the p2x-furs-response-verify branch May 25, 2026 05:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant