# HG changeset patch # User unc0rr # Date 1390680529 -14400 # Node ID 66cab76eb56f097d804eba52ec1e8d852bef120c # Parent 865a4089278d22d0d682900b41a807a84366ab39 Mutual authentication: client side diff -r 865a4089278d -r 66cab76eb56f QTfrontend/hwconsts.cpp.in --- a/QTfrontend/hwconsts.cpp.in Fri Jan 24 22:38:15 2014 +0400 +++ b/QTfrontend/hwconsts.cpp.in Sun Jan 26 00:08:49 2014 +0400 @@ -37,7 +37,7 @@ bool custom_data = false; int cMaxTeams = 8; -int cMinServerVersion = 1; +int cMinServerVersion = 3; QString * cDefaultAmmoStore = new QString( AMMOLINE_DEFAULT_QT AMMOLINE_DEFAULT_PROB AMMOLINE_DEFAULT_DELAY AMMOLINE_DEFAULT_CRATE ); diff -r 865a4089278d -r 66cab76eb56f QTfrontend/net/newnetclient.cpp --- a/QTfrontend/net/newnetclient.cpp Fri Jan 24 22:38:15 2014 +0400 +++ b/QTfrontend/net/newnetclient.cpp Sun Jan 26 00:08:49 2014 +0400 @@ -21,6 +21,7 @@ #include #include #include +#include #include "hwconsts.h" #include "newnetclient.h" @@ -36,7 +37,6 @@ HWNewNet::HWNewNet() : isChief(false), m_game_connected(false), - loginStep(0), netClientState(Disconnected) { m_roomsListModel = new RoomsListModel(this); @@ -238,7 +238,10 @@ void HWNewNet::SendPasswordHash(const QString & hash) { - RawSendNet(QString("PASSWORD%1%2").arg(delimeter).arg(hash)); + // don't send it immediately, only store and check if server asked us for a password + m_passwordHash = hash; + + maybeSendPassword(); } void HWNewNet::ParseCmd(const QStringList & lst) @@ -300,6 +303,28 @@ return; } + if (lst[0] == "SERVER_AUTH") + { + if(lst.size() < 2) + { + qWarning("Net: Malformed SERVER_AUTH message"); + return; + } + + if(lst[2] != m_serverHash) + { + Error("Server authentication error"); + Disconnect(); + } else + { + // empty m_serverHash variable means no authentication was performed + // or server passed authentication + m_serverHash.clear(); + } + + return; + } + if (lst[0] == "PING") { if (lst.size() > 1) @@ -515,6 +540,14 @@ { if (lst[i] == mynick) { + // check if server is authenticated or no authentication was performed at all + if(!m_serverHash.isEmpty()) + { + Error(tr("Server authentication error")); + + Disconnect(); + } + netClientState = InLobby; RawSendNet(QString("LIST")); emit connected(); @@ -578,8 +611,24 @@ if (lst[0] == "ASKPASSWORD") { + // server should send us salt of at least 16 characters + + if(lst.size() < 2 || lst[1].size() < 16) + { + qWarning("Net: Bad ASKPASSWORD message"); + return; + } + emit NickRegistered(mynick); m_nick_registered = true; + + // store server salt + // when this variable is set, it is assumed that server asked us for a password + m_serverSalt = lst[1]; + m_clientSalt = QUuid::createUuid().toString(); + + maybeSendPassword(); + return; } @@ -1083,3 +1132,36 @@ if(!myroom.isEmpty()) JoinRoom(myroom, password); } + +void HWNewNet::maybeSendPassword() +{ +/* When we got password hash, and server asked us for a password, perform mutual authentication: + * at this point we have salt chosen by server + * client sends client salt and hash of secret (password hash) salted with client salt, server salt, + * and static salt (predefined string + protocol number) + * server should respond with hash of the same set in different order. + */ + + if(m_passwordHash.isEmpty() || m_serverSalt.isEmpty()) + return; + + QString hash; + + hash = QCryptographicHash::hash( + m_clientSalt.toAscii() + .append(m_serverSalt.toAscii()) + .append(m_passwordHash) + .append(cProtoVer->toAscii()) + .append("!hedgewars") + , QCryptographicHash::Sha1); + + m_serverHash = QCryptographicHash::hash( + m_serverSalt.toAscii() + .append(m_clientSalt.toAscii()) + .append(m_passwordHash) + .append(cProtoVer->toAscii()) + .append("!hedgewars") + , QCryptographicHash::Sha1); + + RawSendNet(QString("PASSWORD%1%2%1%3").arg(delimeter).arg(hash).arg(m_clientSalt)); +} diff -r 865a4089278d -r 66cab76eb56f QTfrontend/net/newnetclient.h --- a/QTfrontend/net/newnetclient.h Fri Jan 24 22:38:15 2014 +0400 +++ b/QTfrontend/net/newnetclient.h Sun Jan 26 00:08:49 2014 +0400 @@ -77,6 +77,10 @@ QSortFilterProxyModel * m_lobbyPlayersModel; QSortFilterProxyModel * m_roomPlayersModel; QString m_lastRoom; + QString m_passwordHash; + QString m_serverSalt; + QString m_clientSalt; + QString m_serverHash; QStringList cmdbuf; @@ -85,7 +89,8 @@ void ParseCmd(const QStringList & lst); void handleNotice(int n); - int loginStep; + void maybeSendPassword(); + ClientState netClientState; signals: