Skip to content

test(password): make the PHC bound tests reach the bounds they name - #363

Open
Jaro-c wants to merge 1 commit into
developfrom
test/phc-bound-tests-that-can-fail
Open

Jaro-c wants to merge 1 commit into
developfrom
test/phc-bound-tests-that-can-fail

Conversation

@Jaro-c

@Jaro-c Jaro-c commented Sep 8, 2026

Copy link
Copy Markdown
Member

Six tests asserted parameter bounds they never reached. Each built its hash with the literal salt c2FsdA, four bytes decoded, so parsePHC refused it on the salt-length check and every one stayed green with the memory ceiling, the memory floor or the iterations range deleted.

Why this is more than redundant coverage

TestParsePHC_memoryAboveCeilingRejected carries this comment:

A corrupted or attacker-supplied hash with m=4_000_000_000 would cause argon2.IDKey to attempt a multi-TiB allocation and crash the process. The parser must reject it before the key derivation runs.

That names a memory-exhaustion control reachable by any consumer whose stored hash column is poisoned, and the body could not tell whether the control was there. TestVerify_malformedStoredHashIsRejectedBeforeKeyDerivation used the same string and had the same gap.

What changed

Each of the six now builds its hash with the phc() helper, which supplies a full 16-byte salt and 32-byte key, so the parameter bound is the only thing left that can refuse it. Each also asserts the error names the parameter, because err != nil is satisfied by any refusal and that is exactly how these drifted.

Measured after

Deleting the memory range fails both memory tests and the Verify one. Deleting the iterations range fails both iterations tests.

The Verify test now takes 9.54s to fail rather than microseconds, because without the ceiling argon2.IDKey really does attempt the allocation the comment describes. That timing is the clearest evidence the test reaches the control now: previously it returned instantly, having been refused on the salt.

What was never broken

The bounds themselves are enforced, and phc_bounds_internal_test.go tests them properly through the same helper and goes red on the same sabotage. Nothing here weakens or fixes a control. What it fixes is that six tests carrying these names proved nothing, so anyone auditing password_test.go would conclude the ceilings were independently exercised when the only real coverage lived in another file.

Provenance

Found by a MiniMax-M3 pass over the package. It also correctly noted that the internal test file already covers the controls, so it scoped the finding to the misleading tests rather than reporting an undefended control. That accuracy is why this is a test change and not a security fix.

Closes #362

Six tests asserted parameter bounds they never reached. Each built its
hash with the literal salt "c2FsdA", four bytes decoded, so parsePHC
refused it on the salt-length check and every one stayed green with the
memory ceiling, the memory floor or the iterations range deleted.

That is not merely redundant coverage. TestParsePHC_memoryAboveCeiling
Rejected carries a comment saying m=4_000_000_000 would make argon2.IDKey
attempt a multi-TiB allocation and crash the process, and that the parser
must reject it before key derivation runs. The body could not tell
whether it did. Same for TestVerify_malformedStoredHashIsRejectedBefore
KeyDerivation, which used the same string.

Each now builds its hash with the phc helper, which supplies a full
16-byte salt and 32-byte key, so the parameter bound is the only thing
left that can refuse it. Each also asserts the error names the parameter,
because err != nil is satisfied by any refusal and that is how these
drifted.

Measured after. Deleting the memory range fails both memory tests and
the Verify one; deleting the iterations range fails both iterations
tests. The Verify test takes 9.54s to fail rather than microseconds,
because without the ceiling argon2.IDKey really does attempt the
allocation the comment describes. The control was never undefended:
phc_bounds_internal_test.go covers all of it and goes red on the same
sabotage. What was wrong is that the tests carrying these names proved
nothing, so an auditor reading password_test.go would believe the
ceilings were independently exercised.

Closes #362

Signed-off-by: Jose <75870284+Jaro-c@users.noreply.github.com>
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