Skip to content

fix: use constant-time comparisons for admin auth checks#8389

Merged
dpc merged 1 commit into
fedimint:masterfrom
kleysc:fix/constant-time-auth-comparisons
Mar 18, 2026
Merged

fix: use constant-time comparisons for admin auth checks#8389
dpc merged 1 commit into
fedimint:masterfrom
kleysc:fix/constant-time-auth-comparisons

Conversation

@kleysc
Copy link
Copy Markdown
Contributor

@kleysc kleysc commented Mar 15, 2026

Summary

Admin auth in consensus/api.rs (auth gate and change_password), config/setup.rs, and the server UI (lib.rs login handler and dashboard/mod.rs password change) was using plain ==/!= for password comparison, which is vulnerable to timing side-channel attacks.
Replaced with subtle::ConstantTimeEq — same pattern already used in http_auth.rs for FM_FORCE_API_SECRETS.

Also updates two doc comments that referenced PHC format — passwords are stored as plaintext in password.private.

Partial fix for #8277 — Argon2 hashing and storage refactor in a follow-up

Testing

just check passes cleanly. The change is mechanical, ct_eq is a drop-in replacement for == with no behavioral difference

@kleysc kleysc requested review from a team as code owners March 15, 2026 04:46
@kleysc kleysc force-pushed the fix/constant-time-auth-comparisons branch 2 times, most recently from cec0b80 to 7a96a61 Compare March 15, 2026 05:53
Comment thread fedimint-core/src/module/mod.rs Outdated
// Option<ApiAuth>`
/// Authentication uses the hashed user password in PHC format
/// Authentication secret used to verify guardian admin API requests
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
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.

I guess we should remove Eq then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed both PartialEq and Eq from the derive

Comment thread fedimint-core/src/module/mod.rs Outdated
/// Authentication uses the hashed user password in PHC format
/// Authentication secret used to verify guardian admin API requests
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApiAuth(pub String);
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.

We should not make a String pub, I guess.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Made it private. All access now goes through as_str().

Copy link
Copy Markdown
Contributor

@dpc dpc Mar 17, 2026

Choose a reason for hiding this comment

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

Well, the point was to never even access it in any way given its sensitive. We should really only need to compare it with a password (in const time) and that's about it. If there's any other operation that needs to touch it should also be a method. The goal is to make introducing same problem accidentally hard via type system.

This should probably be accompanied with a docstring comment about this goal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a doc comment on ApiAuth covering this

Comment thread fedimint-server-ui/src/lib.rs Outdated
input: LoginInput,
) -> impl IntoResponse {
if auth.0 == input.password {
if bool::from(auth.0.as_bytes().ct_eq(input.password.as_bytes())) {
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.

Make a method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added ApiAuth::verify() — wraps subtle::ConstantTimeEq internally

Copy link
Copy Markdown
Contributor

@dpc dpc left a comment

Choose a reason for hiding this comment

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

Good find. But we should make sure this will not regress and make the String private, I think.

@kleysc kleysc force-pushed the fix/constant-time-auth-comparisons branch from 7a96a61 to b1edf5f Compare March 17, 2026 01:32
@kleysc
Copy link
Copy Markdown
Contributor Author

kleysc commented Mar 17, 2026

Good find. But we should make sure this will not regress and make the String private, I think.

field is private and PartialEq/Eq are gone, so raw == on ApiAuth won't compile anymore

Make the ApiAuth field private and drop PartialEq/Eq so raw == on
passwords can no longer compile. All authentication checks now go
through ApiAuth::verify(), which uses constant-time equality
internally. Adds new() and as_str() accessors for the remaining
call sites.
@kleysc kleysc force-pushed the fix/constant-time-auth-comparisons branch from b1edf5f to 717a5fd Compare March 17, 2026 13:40
@elsirion elsirion added this pull request to the merge queue Mar 18, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Mar 18, 2026
@dpc dpc added this pull request to the merge queue Mar 18, 2026
Merged via the queue into fedimint:master with commit 50a12b3 Mar 18, 2026
22 checks passed
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.

3 participants