Auth: Recovery verification skips dummy work for unknown Users #738

Open
opened 2026-10-02 13:06:57 +00:00 by kayg · 0 comments
Owner

Defensive sec-auth audit, base c4a61e8cf0, requested under #663. Source review only; no product edits.

A4 — Recovery verification does not equalize unknown-User work

Evidence: store.rs:2498 performs Argon2 verification for a User with a
recovery key, including an invalid phrase. Its legacy or missing-User branch
at line 2527 uses hash.is_some() && verify_password(...). Rust short-circuits
the expression, so a missing row does not perform dummy verification. The
same expression remains at round 7a store.rs:3057. api.rs:1008 sends a
missing username to this branch with a nil User ID. The comment that says
work is equalized does not match the code.

Impact: recovery responses have different expensive work for existing and
missing Users. This exposes account existence through a timing side channel.
No timing threshold was measured. This privacy defect does not grant access
and is not classified as a merge blocker by this report.

Fix: always verify a valid dummy hash before combining the verification result
with row presence. Move recovery password work to an admitted blocking worker,
as App Password verification does, so async workers remain available.

Required regression: instrument the shared verifier to confirm one verification
for known and unknown Users, with bounded admission and cancellation-safe work.
Do not assert a narrow wall-clock threshold on the shared build host.

Duplicate check: searched all issue states for authentication, recovery, re-enrolment, auth_time and challenge findings; no matching corrective issue found. Related #3 and #195 are broad auth and authorization work.

Defensive sec-auth audit, base c4a61e8cf090170f35b1bed3350d9de20c83ecd5, requested under #663. Source review only; no product edits. ### A4 — Recovery verification does not equalize unknown-User work Evidence: `store.rs:2498` performs Argon2 verification for a User with a recovery key, including an invalid phrase. Its legacy or missing-User branch at line 2527 uses `hash.is_some() && verify_password(...)`. Rust short-circuits the expression, so a missing row does not perform dummy verification. The same expression remains at round 7a `store.rs:3057`. `api.rs:1008` sends a missing username to this branch with a nil User ID. The comment that says work is equalized does not match the code. Impact: recovery responses have different expensive work for existing and missing Users. This exposes account existence through a timing side channel. No timing threshold was measured. This privacy defect does not grant access and is not classified as a merge blocker by this report. Fix: always verify a valid dummy hash before combining the verification result with row presence. Move recovery password work to an admitted blocking worker, as App Password verification does, so async workers remain available. Required regression: instrument the shared verifier to confirm one verification for known and unknown Users, with bounded admission and cancellation-safe work. Do not assert a narrow wall-clock threshold on the shared build host. Duplicate check: searched all issue states for authentication, recovery, re-enrolment, auth_time and challenge findings; no matching corrective issue found. Related #3 and #195 are broad auth and authorization work.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#738
No description provided.