-
Notifications
You must be signed in to change notification settings - Fork 333
[Brig] Move password verification to the AuthenticationSubsystem, move to Argon2id with new settings. #4271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 20 commits
0cd1e5e
f96f501
0ee6927
33ebee7
4b22fc4
f4ce01a
e8954ca
cda7431
0c48e8d
789bc9b
7ba0b80
a4c3b96
6fc7577
1adaaca
47b0dcb
cca6c0a
dfc9fc0
0cd22c4
7f7adab
b927f37
6455c8e
791eb35
4b724e7
004900e
7ef7275
481bbd1
14f80e9
f4c4804
12f413d
7c47c67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Changed default password hashing from Scrypt to Argon2id. | ||
|
fisx marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,14 +23,18 @@ import Data.Misc | |
| import Data.Qualified | ||
| import Imports | ||
| import Polysemy | ||
| import Wire.API.Password (Password, PasswordStatus) | ||
| import Wire.API.User | ||
| import Wire.API.User.Password | ||
| import Wire.API.User.Password (PasswordResetCode, PasswordResetIdentity) | ||
| import Wire.UserKeyStore | ||
|
|
||
| data AuthenticationSubsystem m a where | ||
| VerifyPassword :: Local UserId -> PlainTextPassword6 -> AuthenticationSubsystem m () | ||
| VerifyPasswordError :: Local UserId -> PlainTextPassword6 -> AuthenticationSubsystem m () | ||
|
elland marked this conversation as resolved.
Outdated
|
||
| CreatePasswordResetCode :: EmailKey -> AuthenticationSubsystem m () | ||
| ResetPassword :: PasswordResetIdentity -> PasswordResetCode -> PlainTextPassword8 -> AuthenticationSubsystem m () | ||
| VerifyPassword :: PlainTextPassword6 -> Password -> AuthenticationSubsystem m (Bool, PasswordStatus) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From perspective of the AuthenticationSubsystem API this looks weird, why does anyone else have access to the hashed password outside this subsystem?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would consider this a temporary problem. This is used in |
||
| VerifyUserPassword :: UserId -> PlainTextPassword6 -> AuthenticationSubsystem r (Bool, PasswordStatus) | ||
| VerifyProviderPassword :: ProviderId -> PlainTextPassword6 -> AuthenticationSubsystem r (Bool, PasswordStatus) | ||
|
elland marked this conversation as resolved.
Outdated
|
||
| -- For testing | ||
| InternalLookupPasswordResetCode :: EmailKey -> AuthenticationSubsystem m (Maybe PasswordResetPair) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -36,15 +36,16 @@ import Polysemy.TinyLog qualified as Log | |||||||
| import System.Logger | ||||||||
| import Wire.API.Allowlists (AllowlistEmailDomains) | ||||||||
| import Wire.API.Allowlists qualified as AllowLists | ||||||||
| import Wire.API.Password | ||||||||
| import Wire.API.Password as Password | ||||||||
| import Wire.API.User | ||||||||
| import Wire.API.User.Password | ||||||||
| import Wire.AuthenticationSubsystem (AuthenticationSubsystem (..)) | ||||||||
| import Wire.AuthenticationSubsystem.Error | ||||||||
| import Wire.EmailSubsystem | ||||||||
| import Wire.HashPassword | ||||||||
| import Wire.PasswordResetCodeStore | ||||||||
| import Wire.PasswordStore | ||||||||
| import Wire.PasswordStore (PasswordStore) | ||||||||
| import Wire.PasswordStore qualified as PasswordStore | ||||||||
| import Wire.Sem.Now | ||||||||
| import Wire.Sem.Now qualified as Now | ||||||||
| import Wire.SessionStore | ||||||||
|
|
@@ -70,22 +71,15 @@ interpretAuthenticationSubsystem :: | |||||||
| interpretAuthenticationSubsystem userSubsystemInterpreter = | ||||||||
| interpret $ | ||||||||
| userSubsystemInterpreter . \case | ||||||||
| VerifyPassword luid password -> verifyPasswordImpl luid password | ||||||||
| VerifyPasswordError luid plaintext -> verifyPasswordErrorImpl luid plaintext | ||||||||
| CreatePasswordResetCode userKey -> createPasswordResetCodeImpl userKey | ||||||||
| ResetPassword ident resetCode newPassword -> resetPasswordImpl ident resetCode newPassword | ||||||||
| VerifyPassword plaintext pwd -> verifyPasswordImpl plaintext pwd | ||||||||
| VerifyUserPassword uid plaintext -> verifyUserPasswordImpl uid plaintext | ||||||||
| VerifyProviderPassword pid plaintext -> verifyProviderPasswordImpl pid plaintext | ||||||||
| -- Testing | ||||||||
| InternalLookupPasswordResetCode userKey -> internalLookupPasswordResetCodeImpl userKey | ||||||||
|
|
||||||||
| verifyPasswordImpl :: | ||||||||
| ( Member PasswordStore r, | ||||||||
| Member (Error AuthenticationSubsystemError) r | ||||||||
| ) => | ||||||||
| Local UserId -> | ||||||||
| PlainTextPassword6 -> | ||||||||
| Sem r () | ||||||||
| verifyPasswordImpl (tUnqualified -> uid) password = do | ||||||||
| p <- lookupHashedPassword uid >>= maybe (throw AuthenticationSubsystemMissingAuth) pure | ||||||||
| unless (Wire.API.Password.verifyPassword password p) $ throw AuthenticationSubsystemBadCredentials | ||||||||
|
|
||||||||
| maxAttempts :: Int32 | ||||||||
| maxAttempts = 3 | ||||||||
|
|
||||||||
|
|
@@ -149,7 +143,9 @@ createPasswordResetCodeImpl target = | |||||||
| Right v -> pure v | ||||||||
|
|
||||||||
| lookupActiveUserIdByUserKey :: | ||||||||
| (Member UserSubsystem r, Member (Input (Local ())) r) => | ||||||||
| ( Member UserSubsystem r, | ||||||||
| Member (Input (Local ())) r | ||||||||
| ) => | ||||||||
| EmailKey -> | ||||||||
| Sem r (Maybe UserId) | ||||||||
| lookupActiveUserIdByUserKey target = | ||||||||
|
|
@@ -230,7 +226,7 @@ resetPasswordImpl ident code pw = do | |||||||
| Log.debug $ field "user" (toByteString uid) . field "action" (val "User.completePasswordReset") | ||||||||
| checkNewIsDifferent uid pw | ||||||||
| hashedPw <- hashPassword pw | ||||||||
| upsertHashedPassword uid hashedPw | ||||||||
| PasswordStore.upsertHashedPassword uid hashedPw | ||||||||
| codeDelete key | ||||||||
| deleteAllCookies uid | ||||||||
| where | ||||||||
|
|
@@ -246,10 +242,10 @@ resetPasswordImpl ident code pw = do | |||||||
|
|
||||||||
| checkNewIsDifferent :: UserId -> PlainTextPassword' t -> Sem r () | ||||||||
| checkNewIsDifferent uid newPassword = do | ||||||||
| mCurrentPassword <- lookupHashedPassword uid | ||||||||
| mCurrentPassword <- PasswordStore.lookupHashedPassword uid | ||||||||
| case mCurrentPassword of | ||||||||
| Just currentPassword | ||||||||
| | (verifyPassword newPassword currentPassword) -> throw AuthenticationSubsystemResetPasswordMustDiffer | ||||||||
| | (Password.verifyPassword newPassword currentPassword) -> throw AuthenticationSubsystemResetPasswordMustDiffer | ||||||||
| _ -> pure () | ||||||||
|
|
||||||||
| verify :: PasswordResetPair -> Sem r (Maybe UserId) | ||||||||
|
|
@@ -266,3 +262,44 @@ resetPasswordImpl ident code pw = do | |||||||
| pure Nothing | ||||||||
| Just PRQueryData {} -> codeDelete k $> Nothing | ||||||||
| Nothing -> pure Nothing | ||||||||
|
|
||||||||
| verifyPasswordImpl :: | ||||||||
| PlainTextPassword6 -> | ||||||||
| Password -> | ||||||||
| Sem r (Bool, PasswordStatus) | ||||||||
| verifyPasswordImpl plaintext password = do | ||||||||
| pure $ Password.verifyPasswordWithStatus plaintext password | ||||||||
|
|
||||||||
| verifyProviderPasswordImpl :: | ||||||||
| (Member PasswordStore r, Member (Error AuthenticationSubsystemError) r) => | ||||||||
| ProviderId -> | ||||||||
| PlainTextPassword6 -> | ||||||||
| Sem r (Bool, PasswordStatus) | ||||||||
| verifyProviderPasswordImpl pid plaintext = do | ||||||||
| -- We type-erase uid here | ||||||||
| password <- | ||||||||
| PasswordStore.lookupHashedProviderPassword pid | ||||||||
| >>= maybe (throw AuthenticationSubsystemBadCredentials) pure | ||||||||
|
Comment on lines
281
to
282
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The call chain ain't long here. Note that this is in the interpreter, not the application code. I expect we call this interpreter only once. |
||||||||
| verifyPasswordImpl plaintext password | ||||||||
|
|
||||||||
| verifyUserPasswordImpl :: | ||||||||
| (Member PasswordStore r, Member (Error AuthenticationSubsystemError) r) => | ||||||||
| UserId -> | ||||||||
| PlainTextPassword6 -> | ||||||||
| Sem r (Bool, PasswordStatus) | ||||||||
| verifyUserPasswordImpl uid plaintext = do | ||||||||
| password <- | ||||||||
| PasswordStore.lookupHashedPassword uid | ||||||||
| >>= maybe (throw AuthenticationSubsystemBadCredentials) pure | ||||||||
|
Comment on lines
292
to
293
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||||||||
| verifyPasswordImpl plaintext password | ||||||||
|
|
||||||||
| verifyPasswordErrorImpl :: | ||||||||
| ( Member PasswordStore r, | ||||||||
| Member (Error AuthenticationSubsystemError) r | ||||||||
| ) => | ||||||||
| Local UserId -> | ||||||||
| PlainTextPassword6 -> | ||||||||
| Sem r () | ||||||||
| verifyPasswordErrorImpl (tUnqualified -> uid) password = do | ||||||||
| unlessM (fst <$> verifyUserPasswordImpl uid password) do | ||||||||
| throw AuthenticationSubsystemBadCredentials | ||||||||
Uh oh!
There was an error while loading. Please reload this page.