Skip to content

refactor(jsignpdf): move the hash algorithm resolution to a dedicated class - #8275

Merged
vitormattos merged 2 commits into
LibreSign:mainfrom
maia-andre:refactor/8145-hash-algorithm-resolver
Sep 6, 2026
Merged

refactor(jsignpdf): move the hash algorithm resolution to a dedicated class#8275
vitormattos merged 2 commits into
LibreSign:mainfrom
maia-andre:refactor/8145-hash-algorithm-resolver

Conversation

@maia-andre

Copy link
Copy Markdown
Contributor

Resolves: #

📝 Summary

Item 3 of the scope described in #8145: the hash algorithm resolution leaves JSignPdfHandler and
becomes a small dedicated class with isolated tests.

The algorithm that signs the document depends on the PDF version — JSignPdf still needs SHA-1 below
PDF 1.6 and refuses it from 1.7 on — so its result describes the signature only. Reusing it for the
other hashes JSignPdf takes would send SHA-1 to a timestamp authority every time the document is
older than PDF 1.6. While these rules lived in the handler as private methods there was no place
where that distinction could be stated or tested.

HashAlgorithmResolver now owns them, one entry point per hash:

  • forSignature(?float $pdfVersion): the version-aware algorithm that goes to --hash-algorithm.
  • requiresPdfVersionUpgradeForSha256(float $pdfVersion): whether the document has to be rewritten
    as PDF 1.6 before the configured SHA-256 is accepted.

extractPdfVersion() stays in the handler: reading the document is the handler's job, not the
policy's.

No behavior change — same version thresholds, same fallback to SHA256, same list of supported
algorithms, same parameters reaching JSignPdf. The TSA hash algorithm as a TSA policy option (items
1 and 2 of the issue) comes in a following PR, and is the reason each hash gets its own entry point
here.

🧪 How to test

vendor/bin/phpunit -c tests/php/phpunit.xml --no-coverage tests/php/Unit/Handler/SignEngine/

HashAlgorithmResolverTest covers the resolution in isolation (no Nextcloud server, only a mocked
PolicyService): every PDF version threshold, the unsupported/empty/unset configured algorithm, and
the PDF version upgrade rule. JSignPdfHandlerTest keeps proving, end to end, which
--hash-algorithm reaches JSignPdf for each document.

⚙️ API / Back‑end changes

  • HashAlgorithmResolver (new) resolves the signature hash algorithm and the PDF version upgrade rule
  • JSignPdfHandler receives it by dependency injection and no longer resolves the hash policy itself
  • Unit and/or integration tests added – required for backend changes

No API, capability or documentation change: the class is internal and autowired.

✅ Checklist

  • I have read and followed the contribution guide.
  • Full unit suite green, except the 4 known AEngineHandlerTest::testSetEngineConfiguresIdentifyMethodsForNoneEngine
    failures: that class passes 26/26 when it runs alone, so they come from state left by other test
    classes, not from this change (it does not touch the certificate engine)
  • 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

Comment thread lib/Handler/SignEngine/JSignPdf/HashAlgorithmResolver.php
… 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
@maia-andre
maia-andre force-pushed the refactor/8145-hash-algorithm-resolver branch from 09233c8 to a989900 Compare September 6, 2026 14:41
@vitormattos

Copy link
Copy Markdown
Member

/backport to stable35

@vitormattos

Copy link
Copy Markdown
Member

/backport to stable34

@vitormattos

Copy link
Copy Markdown
Member

/backport to stable33

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.

2 participants