Skip to content

feat(tsa): let administrators choose the hash algorithm of the timestamp query - #8276

Merged
vitormattos merged 5 commits into
LibreSign:mainfrom
maia-andre:feat/8145-tsa-hash-algorithm
Sep 6, 2026
Merged

feat(tsa): let administrators choose the hash algorithm of the timestamp query#8276
vitormattos merged 5 commits into
LibreSign:mainfrom
maia-andre:feat/8145-tsa-hash-algorithm

Conversation

@maia-andre

Copy link
Copy Markdown
Contributor

Resolves: #8145

📝 Summary

The last of the three parts of #8145: the hash algorithm of the timestamp query becomes an option of
the TSA policy, next to the URL, the policy OID and the authentication.

Until now nobody could choose it. JSignPdf picked its own default — SHA-1 up to 2.3.0, SHA-256 since
3.1.0 — and the authorities in the report answer HTTP 400 when that choice is not the one they
expect. The administrator who configures the authority is the one who knows what it accepts, and in
a multi-tenant instance each TSA rule now carries its own answer.

  • Policy: hash_algorithm inside tsa_settings, default SHA256. SHA1 is not offered: it is
    the algorithm the authorities in the report refuse.
  • JSignPdf: --tsa-hash-algorithm is sent with the configured value, spelled the way JSignPdf
    documents it (SHA-256, with the hyphen, unlike --hash-algorithm).
  • PHP native engine: reads the same policy instead of being pinned to SHA-256, so the option
    does not silently do nothing when that engine signs.
  • The timestamp hash is resolved on its own and never from the signature one, which follows the PDF
    version and returns SHA1 for documents older than PDF 1.6 — exactly the request TSA timestamping always fails with JSignPdf: --tsa-hash-algorithm is never passed, SHA-1 is used #8145 reports as
    rejected.

Existing instances are not affected: the policy value is decoded with SHA256 when the key is
absent, which is what JSignPdf 3.1.0 already does.

🚧 Tasks

🧪 How to test

vendor/bin/phpunit -c tests/php/phpunit.xml --no-coverage --testsuite unit
npm run test

In the interface: Administration settings → LibreSign → Timestamp Authority → Change, turn the
timestamp server on and pick an algorithm. occ config:app:get libresign tsa_settings shows the
stored value.

Manual verification (Nextcloud 35 devcontainer, LibreSign 16.0.0-dev)

  1. Opened the TSA policy editor before and after the change — the select appears between the policy
    OID and the authentication.
  2. Chose SHA-512 and saved.
  3. Reloaded the page and reopened the rule: the value is still SHA-512.
  4. occ config:app:get libresign tsa_settings:
{"url":"https://freetsa.org/tsr","policy_oid":"","auth_type":"none","username":"","hash_algorithm":"SHA512"}

🎨 UI / Front‑end changes

  • New TSA hash algorithm select in the TSA rule editor (SHA-256, SHA-384, SHA-512)
  • Screenshots before/after
  • Components, Unit (with vitest) and/or e2e (with Playwright) tests added - Required
  • Accessibility verified – the select is the same NcSelect already used for the authentication
    type, with its own label
🏚️ Before 🏡 After
Algorithms offered Saved and reopened

⚙️ API / Back‑end changes

  • TsaPolicyValue gains hash_algorithm (default SHA256, validated against SHA256/SHA384/SHA512)
  • HashAlgorithmResolver::forTsa() answers the timestamp hash from the TSA policy, never from the signature one
  • JSignPdfHandler sends --tsa-hash-algorithm; PhpNativeHandler passes the same value to the signer library
  • Unit and/or integration tests added – required for backend changes

No API or capability change: the policy value keeps the same shape, with one more key.

✅ Checklist

  • I have read and followed the contribution guide.
  • PHP unit suite: 3659 tests, only the 4 known AEngineHandlerTest::testSetEngineConfiguresIdentifyMethodsForNoneEngine
    failures, which come from state left by other test classes (that class passes 26/26 alone)
  • Frontend: 3118 vitest tests green, vue-tsc --noEmit clean
  • Infection on the changed lines: 19/19 mutants killed (100% MSI, 100% mutation code coverage)
  • Psalm and php-cs-fixer clean on the changed files

🤖 AI (if applicable)

  • The content of this PR was partially or fully generated using AI

@github-project-automation github-project-automation Bot moved this to 0. Backlog in Roadmap Sep 6, 2026
@maia-andre
maia-andre force-pushed the feat/8145-tsa-hash-algorithm branch from 0b2af67 to 10f18a5 Compare September 6, 2026 14:39
@maia-andre

Copy link
Copy Markdown
Contributor Author

Rebased on the updated #8275: the JSignPdf classes moved to SignEngine/JSignPdf/ there, so this branch now carries that commit too. Nothing changed in what this PR itself does.

… class

The hash algorithm that signs a document depends on the PDF version, so it
is not a valid answer for the other hashes JSignPdf takes: reusing it would
send SHA1 to a timestamp authority whenever the document is older than PDF
1.6. Issue LibreSign#8145 asks for each hash to be resolved on its own, which is hard
to guarantee while the rules live inside the handler as private methods.

Move getHashAlgorithm(), getHashAlgorithmForPdfVersion(),
validateHashAlgorithm() and requiresPdfVersionUpgradeForSha256() to
HashAlgorithmResolver, where each hash gets its own entry point and can be
tested in isolation. The handler keeps reading the PDF version, the only
part that is about the document and not about the policy.

No behavior change: the same version thresholds, the same fallback to
SHA256 and the same supported algorithms.

Signed-off-by: André Maia <andrefnkmm@gmail.com>
Assisted-by: Claude Code:claude-opus-5
Both the handler and the resolver carry rules that only make sense for
JSignPdf — the PDF version thresholds come from its own HashAlgorithm enum —
so they move to SignEngine/JSignPdf/ and the resolver keeps a name that does
not need to repeat the engine.

Pkcs12Handler used to build the handler class name at runtime from the
configured engine ('...\SignEngine\' . ucfirst($property)), a string no
static analysis, IDE rename or grep could follow: moving the class would
only fail when a document was signed. It now resolves through an explicit
map, so a name that is not an engine answers the same friendly error instead
of reaching the container.

Signed-off-by: André Maia <andrefnkmm@gmail.com>
Assisted-by: Claude Code:claude-opus-5
The hash of the timestamp query was never configurable: JSignPdf decided it
alone, and LibreSign#8145 reports authorities rejecting the request because of that
choice. It belongs to the TSA settings, next to the URL, the policy OID and
the authentication, so every tenant that configures its own authority can
also say what that authority expects.

SHA256 is the default and keeps what JSignPdf 3.1.0 already does. SHA1 is
not offered: it is the algorithm the authorities in the report refuse.

Signed-off-by: André Maia <andrefnkmm@gmail.com>
Assisted-by: Claude Code:claude-opus-5
…uthority

JSignPdf decided the timestamp hash by itself, and the authorities in LibreSign#8145
answer HTTP 400 when that choice is not the one they expect. Send
--tsa-hash-algorithm from the TSA policy instead, spelled the way JSignPdf
wants it (SHA-256, with the hyphen, unlike --hash-algorithm).

The resolver answers this hash on its own, never from the signature one:
that one follows the PDF version and would ask for SHA1 on any document
older than PDF 1.6, which is the request the authority rejects.

The php native engine reads the same policy, so it stops being pinned to
SHA-256 and follows what the administrator chose.

Signed-off-by: André Maia <andrefnkmm@gmail.com>
Assisted-by: Claude Code:claude-opus-5
The algorithm of the timestamp query is now part of the TSA policy, so it
belongs next to the URL, the policy OID and the authentication: the
administrator who knows which authority is being used is the one who knows
what it accepts.

The select offers SHA-256, SHA-384 and SHA-512, spelled the way the
authorities document them, and defaults to SHA-256.

Signed-off-by: André Maia <andrefnkmm@gmail.com>
Assisted-by: Claude Code:claude-opus-5
@maia-andre
maia-andre force-pushed the feat/8145-tsa-hash-algorithm branch from 10f18a5 to f545682 Compare September 6, 2026 14:46
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 80.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ngs/PolicyWorkbench/settings/tsa/TsaRuleEditor.vue 66.66% 0 Missing and 2 partials ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Files with missing lines Coverage Δ
...ews/Settings/PolicyWorkbench/settings/tsa/model.ts 95.23% <100.00%> (ø)
...ngs/PolicyWorkbench/settings/tsa/TsaRuleEditor.vue 81.57% <66.66%> (ø)

... and 211 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-project-automation github-project-automation Bot moved this from 0. Backlog to 1. to do in Roadmap Sep 6, 2026
@vitormattos

Copy link
Copy Markdown
Member

/backport to stable35

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 4. to release

Development

Successfully merging this pull request may close these issues.

TSA timestamping always fails with JSignPdf: --tsa-hash-algorithm is never passed, SHA-1 is used

3 participants