--- a/qmlfrontend/net_session.cpp Wed May 29 01:05:20 2019 +0200
+++ b/qmlfrontend/net_session.cpp Thu May 30 18:31:02 2019 +0200
@@ -1,12 +1,15 @@
#include "net_session.h"
+#include <QUuid>
+
#include "players_model.h"
#include "rooms_model.h"
NetSession::NetSession(QObject *parent)
: QObject(parent),
m_playersModel(new PlayersListModel()),
- m_roomsModel(new RoomsListModel()) {}
+ m_roomsModel(new RoomsListModel()),
+ m_sessionState(NotConnected) {}
NetSession::~NetSession() { close(); }
@@ -33,14 +36,14 @@
QString NetSession::nickname() const { return m_nickname; }
-QString NetSession::password() const { return m_password; }
-
NetSession::SessionState NetSession::sessionState() const {
return m_sessionState;
}
QString NetSession::room() const { return m_room; }
+QString NetSession::passwordHash() const { return m_passwordHash; }
+
void NetSession::setUrl(const QUrl &url) {
if (m_url == url) return;
@@ -55,11 +58,13 @@
emit nicknameChanged(m_nickname);
}
-void NetSession::setPassword(const QString &password) {
- if (m_password == password) return;
+void NetSession::setPasswordHash(const QString &passwordHash) {
+ if (m_passwordHash == passwordHash) return;
- m_password = password;
- emit passwordChanged(m_password);
+ m_passwordHash = passwordHash;
+ emit passwordHashChanged(m_passwordHash);
+
+ if (m_sessionState == Authentication) sendPassword();
}
void NetSession::setRoom(const QString &room) {
@@ -174,7 +179,23 @@
void NetSession::handleAddTeam(const QStringList ¶meters) {}
-void NetSession::handleAskPassword(const QStringList ¶meters) {}
+void NetSession::handleAskPassword(const QStringList ¶meters) {
+ if (parameters.length() != 1 || parameters[0].length() < 16) {
+ qWarning("Bad ASKPASSWORD message");
+ return;
+ }
+
+ setSessionState(Authentication);
+
+ m_serverSalt = parameters[0];
+ m_clientSalt = QUuid::createUuid().toString();
+
+ if (m_passwordHash.isEmpty()) {
+ emit passwordAsked();
+ } else {
+ sendPassword();
+ }
+}
void NetSession::handleBanList(const QStringList ¶meters) {}
@@ -306,6 +327,37 @@
m_socket->write(message.join('\n').toUtf8() + "\n\n");
}
+void NetSession::sendPassword() {
+ /* 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 =
+ QCryptographicHash::hash(m_clientSalt.toLatin1()
+ .append(m_serverSalt.toLatin1())
+ .append(m_passwordHash)
+ .append(QByteArray::number(cProtocolVersion))
+ .append("!hedgewars"),
+ QCryptographicHash::Sha1)
+ .toHex();
+
+ m_serverHash =
+ QCryptographicHash::hash(m_serverSalt.toLatin1()
+ .append(m_clientSalt.toLatin1())
+ .append(m_passwordHash)
+ .append(QByteArray::number(cProtocolVersion))
+ .append("!hedgewars"),
+ QCryptographicHash::Sha1)
+ .toHex();
+
+ send("PASSWORD", QStringList{hash, m_clientSalt});
+}
+
void NetSession::setSessionState(NetSession::SessionState sessionState) {
if (m_sessionState == sessionState) return;
--- a/qmlfrontend/net_session.h Wed May 29 01:05:20 2019 +0200
+++ b/qmlfrontend/net_session.h Thu May 30 18:31:02 2019 +0200
@@ -18,13 +18,13 @@
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(QAbstractSocket::SocketState state READ state NOTIFY stateChanged)
Q_PROPERTY(QString nickname READ nickname WRITE setNickname NOTIFY nicknameChanged)
- Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
Q_PROPERTY(SessionState sessionState READ sessionState NOTIFY sessionStateChanged)
Q_PROPERTY(QString room READ room NOTIFY roomChanged)
+ Q_PROPERTY(QString passwordHash READ passwordHash WRITE setPasswordHash NOTIFY passwordHashChanged)
// clang-format on
public:
- enum SessionState { NotConnected, Login, Lobby, Room, Game };
+ enum SessionState { NotConnected, Login, Authentication, Lobby, Room, Game };
Q_ENUMS(SessionState)
explicit NetSession(QObject *parent = nullptr);
@@ -34,9 +34,9 @@
QAbstractSocket::SocketState state() const;
QString nickname() const;
- QString password() const;
SessionState sessionState() const;
QString room() const;
+ QString passwordHash() const;
public slots:
void open();
@@ -44,17 +44,18 @@
void setUrl(const QUrl &url);
void setNickname(const QString &nickname);
- void setPassword(const QString &password);
+ void setPasswordHash(const QString &passwordHash);
signals:
void urlChanged(const QUrl url);
void stateChanged(QAbstractSocket::SocketState state);
void nicknameChanged(const QString &nickname);
- void passwordChanged(const QString &password);
void sessionStateChanged(SessionState sessionState);
void warning(const QString &message);
void error(const QString &message);
void roomChanged(const QString &room);
+ void passwordHashChanged(const QString &passwordHash);
+ void passwordAsked();
private slots:
void onReadyRead();
@@ -102,6 +103,9 @@
void send(const QString &message, const QString ¶m);
void send(const QString &message, const QStringList ¶meters);
void send(const QStringList &message);
+
+ void sendPassword();
+
void setSessionState(SessionState sessionState);
void setRoom(const QString &room);
@@ -111,11 +115,14 @@
QSharedPointer<PlayersListModel> m_playersModel;
QSharedPointer<RoomsListModel> m_roomsModel;
QString m_nickname;
- QString m_password;
QStringList m_buffer;
SessionState m_sessionState;
QString m_serverAuthHash;
QString m_room;
+ QString m_serverSalt;
+ QString m_serverHash;
+ QString m_clientSalt;
+ QString m_passwordHash;
Q_DISABLE_COPY(NetSession)
};