mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-25 11:56:11 -07:00
[Security] Add challenge-response auth with scrypt verifiers and stop storing plaintext passwords
Challenge-response authentication: the client derives a scrypt verifier (RFC 7914, EVP_PBE_scrypt, N=32768, r=8, p=1) and authenticates with HMAC-SHA256(key, nonce), so neither the password nor its hash is transmitted. The stored format becomes "$scrypt$<n>$<r>$<p>$<salt>$<verifier>" and Response_PasswordSalt now carries the cost parameters. Strict servers only accept scrypt verifiers; legacy accounts are migrated after a successful login. Fix #344 for challenge-response servers: a saved profile stores the derived verifier under the password key instead of the plaintext password. The connect dialog loads it without revealing it, autoconnect passes it through, the change-password dialog no longer prefills the old password field with it, and the client only persists the verifier when "Save password" is checked. Took 3 minutes Took 1 minute Took 10 seconds Took 7 minutes
This commit is contained in:
parent
45d97cb8d2
commit
fb7f7dde55
33 changed files with 676 additions and 23 deletions
|
|
@ -85,6 +85,11 @@ public:
|
|||
{
|
||||
return QMap<QString, bool>();
|
||||
}
|
||||
/** @brief True when only challenge-response logins are accepted (strict mode). */
|
||||
virtual bool requiresChallengeResponseAuth() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void addClient(Server_ProtocolHandler *player);
|
||||
void removeClient(Server_ProtocolHandler *player);
|
||||
QList<QString> getOnlineModeratorList() const;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,14 @@ public:
|
|||
{
|
||||
return {};
|
||||
}
|
||||
virtual QString getUserPasswordData(const QString & /* user */)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
virtual bool submitPasswordVerifier(const QString & /* user */, const QString & /* passwordVerifier */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual QMap<QString, ServerInfo_User> getBuddyList(const QString & /* name */)
|
||||
{
|
||||
return QMap<QString, ServerInfo_User>();
|
||||
|
|
|
|||
|
|
@ -44,6 +44,22 @@ Server_ProtocolHandler::~Server_ProtocolHandler()
|
|||
{
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::setAuthNonce(const QByteArray &nonce)
|
||||
{
|
||||
authNonce = nonce;
|
||||
authNonceCreated = QDateTime::currentDateTimeUtc();
|
||||
}
|
||||
|
||||
bool Server_ProtocolHandler::isAuthNonceValid(const QByteArray &nonce) const
|
||||
{
|
||||
return !authNonce.isEmpty() && authNonce == nonce && authNonceCreated.secsTo(QDateTime::currentDateTimeUtc()) < 60;
|
||||
}
|
||||
|
||||
void Server_ProtocolHandler::clearAuthNonce()
|
||||
{
|
||||
authNonce.clear();
|
||||
}
|
||||
|
||||
// This function must only be called from the thread this object lives in.
|
||||
// Except when the server is shutting down.
|
||||
// The thread must not hold any server locks when calling this (e.g. clientsLock, roomsLock).
|
||||
|
|
@ -507,6 +523,16 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
// In strict mode only challenge-response logins are accepted.
|
||||
if (server->requiresChallengeResponseAuth() &&
|
||||
(!cmd.has_hashed_password() || cmd.hashed_password().rfind("$challenge$", 0) != 0)) {
|
||||
auto *re = new Response_Login;
|
||||
re->set_denied_reason_str("Client upgrade required");
|
||||
re->add_missing_features("challenge_response_auth");
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespClientUpdateRequired;
|
||||
}
|
||||
|
||||
// check client feature set against server feature set
|
||||
FeatureSet features;
|
||||
QMap<QString, bool> receivedClientFeatures;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include "server.h"
|
||||
#include "server_abstractuserinterface.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
#include <libcockatrice/protocol/pb/server_message.pb.h>
|
||||
|
|
@ -55,6 +57,8 @@ protected:
|
|||
bool acceptsUserListChanges;
|
||||
bool acceptsRoomListChanges;
|
||||
bool idleClientWarningSent;
|
||||
QByteArray authNonce;
|
||||
QDateTime authNonceCreated;
|
||||
virtual void logDebugMessage(const QString & /* message */)
|
||||
{
|
||||
}
|
||||
|
|
@ -124,6 +128,13 @@ public:
|
|||
return databaseInterface;
|
||||
}
|
||||
|
||||
/** @brief Store a fresh challenge nonce for the next challenge-response login attempt. */
|
||||
void setAuthNonce(const QByteArray &nonce);
|
||||
/** @brief True if nonce matches the pending one and was issued less than 60 seconds ago. */
|
||||
bool isAuthNonceValid(const QByteArray &nonce) const;
|
||||
/** @brief Invalidate the pending nonce (single-use). */
|
||||
void clearAuthNonce();
|
||||
|
||||
int getLastCommandTime() const
|
||||
{
|
||||
return timeRunning - lastDataReceived;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue