This commit is contained in:
1 parent
7975e7c36f
commit
2d95b705cd
6 files changed
+108
-9
No files matched your search
@@ -7,3 +7,6 @@
|
||||
[submodule "3rdparty/qrcode-svg"]
|
||||
path = 3rdparty/qrcode-svg
|
||||
url = https://github.com/papnkukn/qrcode-svg.git
|
||||
[submodule "3rdparty/Bcrypt.cpp"]
|
||||
path = 3rdparty/Bcrypt.cpp
|
||||
url = https://github.com/hilch/Bcrypt.cpp.git
|
||||
+1
Submodule 3rdparty/Bcrypt.cpp added at 0d18b6a99e.
Vendored
+13
@@ -1,2 +1,15 @@
|
||||
add_subdirectory(qmsgpack SYSTEM)
|
||||
add_subdirectory(QtZeroConf SYSTEM)
|
||||
|
||||
# Bcrypt.cpp's upstream CMake file still requires compatibility with CMake 3.2,
|
||||
# which is no longer supported by current CMake releases.
|
||||
add_library(bcrypt STATIC
|
||||
Bcrypt.cpp/src/bcrypt.cpp
|
||||
Bcrypt.cpp/src/blowfish.cpp
|
||||
)
|
||||
set_target_properties(bcrypt PROPERTIES
|
||||
AUTOMOC OFF
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
)
|
||||
target_include_directories(bcrypt SYSTEM PUBLIC Bcrypt.cpp/include)
|
||||
target_include_directories(bcrypt PRIVATE Bcrypt.cpp/src)
|
||||
@@ -181,6 +181,11 @@ target_link_libraries(evcharger-app PUBLIC
|
||||
goecommon
|
||||
)
|
||||
|
||||
target_link_libraries(evcharger-app PRIVATE bcrypt)
|
||||
target_include_directories(evcharger-app PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/Bcrypt.cpp/src
|
||||
)
|
||||
|
||||
install(TARGETS evcharger-app
|
||||
BUNDLE DESTINATION .
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "msgpack.h"
|
||||
#include "node_blf.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -23,6 +24,33 @@ auto enumToString(const QEnum value)
|
||||
{
|
||||
return QMetaEnum::fromType<QEnum>().valueToKey(std::to_underlying(value));
|
||||
}
|
||||
|
||||
QString deriveBcryptPassword(const QString &password, const QString &serial)
|
||||
{
|
||||
constexpr qsizetype bcryptSaltBytes = 16;
|
||||
constexpr auto bcryptCost = 8;
|
||||
|
||||
auto serialBytes = serial.toUtf8();
|
||||
if (serialBytes.size() < bcryptSaltBytes)
|
||||
serialBytes.prepend(QByteArray(bcryptSaltBytes - serialBytes.size(), '\0'));
|
||||
else if (serialBytes.size() > bcryptSaltBytes)
|
||||
serialBytes.truncate(bcryptSaltBytes);
|
||||
|
||||
// This reproduces bcryptjs' "$2a$08$" + encodeBase64(serial.padStart(16, "\\0"), 16).
|
||||
QByteArray salt(_SALT_LEN, '\0');
|
||||
bcrypt_gensalt('a', bcryptCost, reinterpret_cast<u_int8_t *>(serialBytes.data()), salt.data());
|
||||
|
||||
const auto passwordSha256 = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256).toHex();
|
||||
QByteArray fullHash(61, '\0');
|
||||
node_bcrypt(passwordSha256.constData(), passwordSha256.size(), salt.constData(), fullHash.data());
|
||||
|
||||
salt.truncate(qstrlen(salt.constData()));
|
||||
fullHash.truncate(qstrlen(fullHash.constData()));
|
||||
if (!fullHash.startsWith(salt))
|
||||
return {};
|
||||
|
||||
return QString::fromLatin1(fullHash.mid(salt.size(), 32));
|
||||
}
|
||||
}
|
||||
|
||||
DeviceConnection::DeviceConnection(QObject *parent) :
|
||||
@@ -271,6 +299,32 @@ void DeviceConnection::messageReceived(const QVariant &variant)
|
||||
m_token2 = token2Variant.toString();
|
||||
}
|
||||
|
||||
m_passwordHashAlgorithm = PasswordHashAlgorithm::Pbkdf2;
|
||||
if (auto iter = map.find("hash"); iter != std::cend(map))
|
||||
{
|
||||
const auto &hashVariant = *iter;
|
||||
if (hashVariant.typeId() != QMetaType::QString)
|
||||
{
|
||||
emit logMessage(tr("Received authRequired with a non-string hash algorithm!"));
|
||||
m_passwordHashAlgorithm = PasswordHashAlgorithm::Unsupported;
|
||||
emit authImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto hashAlgorithm = hashVariant.toString();
|
||||
if (hashAlgorithm == "pbkdf2")
|
||||
m_passwordHashAlgorithm = PasswordHashAlgorithm::Pbkdf2;
|
||||
else if (hashAlgorithm == "bcrypt")
|
||||
m_passwordHashAlgorithm = PasswordHashAlgorithm::Bcrypt;
|
||||
else
|
||||
{
|
||||
emit logMessage(tr("Received authRequired with unsupported hash algorithm %0!").arg(hashAlgorithm));
|
||||
m_passwordHashAlgorithm = PasswordHashAlgorithm::Unsupported;
|
||||
emit authImpossible();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_password.isEmpty())
|
||||
emit authRequired();
|
||||
else
|
||||
@@ -438,15 +492,30 @@ void DeviceConnection::sendMessage(const QVariantMap &variant)
|
||||
|
||||
void DeviceConnection::sendAuth(const QString &password)
|
||||
{
|
||||
const QString data = password;
|
||||
const QString salt = m_serial;
|
||||
constexpr auto iterations = 100000;
|
||||
constexpr auto keylen = 64;
|
||||
|
||||
m_password = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha512, data.toUtf8(), salt.toUtf8(), iterations, keylen).toBase64();
|
||||
|
||||
if (m_password.size() > 32)
|
||||
m_password.resize(32);
|
||||
switch (m_passwordHashAlgorithm)
|
||||
{
|
||||
case PasswordHashAlgorithm::Pbkdf2:
|
||||
{
|
||||
constexpr auto iterations = 100000;
|
||||
constexpr auto keylen = 64;
|
||||
m_password = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha512, password.toUtf8(), m_serial.toUtf8(), iterations, keylen).toBase64();
|
||||
if (m_password.size() > 32)
|
||||
m_password.resize(32);
|
||||
break;
|
||||
}
|
||||
case PasswordHashAlgorithm::Bcrypt:
|
||||
m_password = deriveBcryptPassword(password, m_serial);
|
||||
if (m_password.isEmpty())
|
||||
{
|
||||
emit logMessage(tr("Could not derive bcrypt authentication hash!"));
|
||||
emit authImpossible();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case PasswordHashAlgorithm::Unsupported:
|
||||
emit authImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_websocket.state() != QAbstractSocket::ConnectedState)
|
||||
return;
|
||||
|
||||
@@ -81,6 +81,13 @@ private slots:
|
||||
void handshakeInterruptedOnError(const QSslError &error);
|
||||
|
||||
private:
|
||||
enum class PasswordHashAlgorithm
|
||||
{
|
||||
Pbkdf2,
|
||||
Bcrypt,
|
||||
Unsupported
|
||||
};
|
||||
|
||||
void sendAuthInternal();
|
||||
void authResponse(const QString &type, const QVariantMap &message);
|
||||
|
||||
@@ -100,6 +107,7 @@ private:
|
||||
|
||||
QString m_token1;
|
||||
QString m_token2;
|
||||
PasswordHashAlgorithm m_passwordHashAlgorithm{PasswordHashAlgorithm::Pbkdf2};
|
||||
|
||||
QString m_password;
|
||||
|
||||
|
||||
Reference in new issue
Block a user