Skip to content

fix(auth): route PBKDF2 getEncoded() through the SecretKey interface for Adobe 2025 JPMS - #3301

Merged
bpamiri merged 1 commit into
developfrom
peter/issue-3300-hasher-adobe2025-jpms
Jul 7, 2026
Merged

fix(auth): route PBKDF2 getEncoded() through the SecretKey interface for Adobe 2025 JPMS#3301
bpamiri merged 1 commit into
developfrom
peter/issue-3300-hasher-adobe2025-jpms

Conversation

@bpamiri

@bpamiri bpamiri commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Closes #3300 — the only new cross-engine failure from the #2962 feature merges (verified by diffing matrix run 28815315339 against the 2026-06-28 baseline: Lucee 6/7, Adobe 2023, and BoxLang show no new failures).

Root cause

SecretKeyFactory.generateSecret() returns a com.sun.crypto.provider.PBKDF2KeyImpl — a JDK-internal class java.base does not open. Adobe 2025's reflection layer bulk-setAccessibles the concrete class's methods when dispatching .getEncoded() and its JVM rejects that with InaccessibleObjectException, erroring 17 of 24 PasswordHasherSpec specs on every database leg. Lucee, BoxLang, and Adobe ≤2023 tolerate the direct call — which is why local Adobe 2023 verification (24/24) missed it.

Fix

Invoke getEncoded() through the exported javax.crypto.SecretKey interface Method object — public interface methods need no opens. Output is byte-identical (same key object, same method), so stored hashes are unaffected.

Also adds Cross-Engine Invariant 14 to CLAUDE.md documenting the JPMS crash class (member calls on JDK-internal implementation types) and the Adobe-2023-green-doesn't-cover-2025 trap.

Verification (local docker, db=sqlite, testBundles=wheels.tests.specs.auth.PasswordHasherSpec)

Engine develop HEAD this branch
Adobe 2025 (image built from tools/docker/adobe2025) 7 pass / 17 error (reproduces CI exactly) 24 pass / 0 / 0
Adobe 2023 24/0/0 24/0/0
Lucee 7 24/0/0 24/0/0

No changelog fragment: #3288 has not shipped in a tagged release, so this folds into its existing added entry.

🤖 Generated with Claude Code

…for Adobe 2025 JPMS

SecretKeyFactory.generateSecret() returns a com.sun.crypto.provider.PBKDF2KeyImpl,
a JDK-internal class java.base does not open. Adobe 2025's reflection layer
bulk-setAccessibles the concrete class's methods when dispatching a member call
and its JVM rejects that with InaccessibleObjectException, erroring 17 of 24
PasswordHasherSpec specs on every database leg (matrix run 28815315339) — the
only new cross-engine failure from the 2962 feature merges. Lucee, BoxLang, and
Adobe <= 2023 tolerate the direct call.

Invoke getEncoded() via the exported javax.crypto.SecretKey interface Method
object instead; public interface methods need no opens. Adds Cross-Engine
Invariant 14 to CLAUDE.md so the pattern is checked going forward.

Closes #3300

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <petera@pai.com>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer

TL;DR — This PR fixes #3300: PasswordHasher.$deriveKey() crashed on Adobe 2025 because it called .getEncoded() directly on the com.sun.crypto.provider.PBKDF2KeyImpl returned by SecretKeyFactory.generateSecret() — a JDK-internal class java.base does not open, which Adobe 2025's reflection layer trips over with InaccessibleObjectException. The fix routes the call through the exported javax.crypto.SecretKey interface's Method object, and documents the crash class as Cross-Engine Invariant 14. The change is minimal, correct, and empirically verified on the exact failing engine. Verdict: approve.

Correctness

The reflective path is sound. Method.invoke on the interface's getEncoded method dispatches virtually to the concrete PBKDF2KeyImpl implementation, so the returned bytes are identical to the old direct call — stored hashes are unaffected, as the PR body claims. getMethod("getEncoded", JavaCast("null","")) and invoke(key, JavaCast("null","")) correctly resolve the null varargs to "no parameters" (vendor/wheels/auth/PasswordHasher.cfc:253-256).

Cross-engine

No concern. The null-varargs interop is the one place this could have gone engine-specific, but the PR verifies 24/24 on Adobe 2025 (the failing engine), Adobe 2023, and Lucee 7, with the matrix run covering Lucee 6/7 + BoxLang. Had CFML mapped the null to a single-element [null] array, getMethod would throw NoSuchMethodException on every engine rather than pass — so the empirical green across engines confirms the intended resolution. This is exactly the kind of fix Invariant 14 is meant to prevent recurring.

Tests

Covered by the existing vendor/wheels/tests/specs/auth/PasswordHasherSpec.cfc — these are the 17/24 specs that were erroring on Adobe 2025 and now pass. A behavior-preserving cross-engine fix to an already-covered code path needs no new spec.

Docs

CLAUDE.md Invariant 14 is a clear, actionable writeup of the JPMS crash class and the "Adobe-2023-green != Adobe-2025-covered" trap (CLAUDE.md:53). The "no changelog fragment" rationale checks out: changelog.d/3155-password-hasher.added.md is still unreleased (present in changelog.d/, not yet promoted), so this correctly folds into that existing added entry rather than adding a redundant fragment.

Commits

fix(auth): route PBKDF2 getEncoded() through the SecretKey interface for Adobe 2025 JPMS — valid type/scope, 88-char header (<=100), not ALL-CAPS. Carries a Signed-off-by: trailer (DCO satisfied) and describes the why, not just the what.


Non-blocking nit (optional): Class.forName("javax.crypto.SecretKey").getMethod(...) runs on every $deriveKey() call. The reflection lookup is negligible next to PBKDF2's iteration cost, so this is purely cosmetic — but if you ever want to shave it, the Method object is immutable and could be resolved once into variables scope in init()/config(). Not required for merge.

@bpamiri
bpamiri merged commit 47fdb0a into develop Jul 7, 2026
10 checks passed
@bpamiri
bpamiri deleted the peter/issue-3300-hasher-adobe2025-jpms branch July 7, 2026 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PasswordHasher errors on Adobe 2025: JPMS blocks reflection into PBKDF2KeyImpl

1 participant