mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Fix password generator responses (#7404)
* Respond directly to the current client instead of broadcasting * Append requestID to generate-password response
This commit is contained in:
parent
6791024995
commit
7284a8062a
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -16,65 +16,22 @@
|
||||
*/
|
||||
|
||||
#include "BrowserAction.h"
|
||||
|
||||
#include "BrowserMessageBuilder.h"
|
||||
#include "BrowserService.h"
|
||||
#include "BrowserSettings.h"
|
||||
#include "BrowserShared.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Global.h"
|
||||
#include "core/Tools.h"
|
||||
#include "gui/PasswordGeneratorWidget.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <botan/sodium.h>
|
||||
|
||||
using namespace Botan::Sodium;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
ERROR_KEEPASS_DATABASE_NOT_OPENED = 1,
|
||||
ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED = 2,
|
||||
ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED = 3,
|
||||
ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE = 4,
|
||||
ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED = 5,
|
||||
ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED = 6,
|
||||
ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE = 7,
|
||||
ERROR_KEEPASS_ASSOCIATION_FAILED = 8,
|
||||
ERROR_KEEPASS_KEY_CHANGE_FAILED = 9,
|
||||
ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED = 10,
|
||||
ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND = 11,
|
||||
ERROR_KEEPASS_INCORRECT_ACTION = 12,
|
||||
ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED = 13,
|
||||
ERROR_KEEPASS_NO_URL_PROVIDED = 14,
|
||||
ERROR_KEEPASS_NO_LOGINS_FOUND = 15,
|
||||
ERROR_KEEPASS_NO_GROUPS_FOUND = 16,
|
||||
ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17,
|
||||
ERROR_KEEPASS_NO_VALID_UUID_PROVIDED = 18
|
||||
};
|
||||
}
|
||||
#include <QLocalSocket>
|
||||
|
||||
const int BrowserAction::MaxUrlLength = 256;
|
||||
|
||||
BrowserAction::BrowserAction()
|
||||
{
|
||||
QObject::connect(browserService(),
|
||||
&BrowserService::passwordGenerated,
|
||||
browserService(),
|
||||
[=](const QString& password, const QString& nonce) {
|
||||
auto newNonce = incrementNonce(nonce);
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
message["password"] = password;
|
||||
|
||||
browserService()->sendPassword(buildResponse("generate-password", message, newNonce));
|
||||
});
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::processClientMessage(const QJsonObject& json)
|
||||
QJsonObject BrowserAction::processClientMessage(QLocalSocket* socket, const QJsonObject& json)
|
||||
{
|
||||
if (json.isEmpty()) {
|
||||
return getErrorReply("", ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED);
|
||||
@ -100,13 +57,13 @@ QJsonObject BrowserAction::processClientMessage(const QJsonObject& json)
|
||||
}
|
||||
}
|
||||
|
||||
return handleAction(json);
|
||||
return handleAction(socket, json);
|
||||
}
|
||||
|
||||
// Private functions
|
||||
///////////////////////
|
||||
|
||||
QJsonObject BrowserAction::handleAction(const QJsonObject& json)
|
||||
QJsonObject BrowserAction::handleAction(QLocalSocket* socket, const QJsonObject& json)
|
||||
{
|
||||
QString action = json.value("action").toString();
|
||||
|
||||
@ -121,7 +78,7 @@ QJsonObject BrowserAction::handleAction(const QJsonObject& json)
|
||||
} else if (action.compare("get-logins") == 0) {
|
||||
return handleGetLogins(json, action);
|
||||
} else if (action.compare("generate-password") == 0) {
|
||||
return handleGeneratePassword(json, action);
|
||||
return handleGeneratePassword(socket, json, action);
|
||||
} else if (action.compare("set-login") == 0) {
|
||||
return handleSetLogin(json, action);
|
||||
} else if (action.compare("lock-database") == 0) {
|
||||
@ -152,23 +109,18 @@ QJsonObject BrowserAction::handleChangePublicKeys(const QJsonObject& json, const
|
||||
}
|
||||
|
||||
m_associated = false;
|
||||
unsigned char pk[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_box_SECRETKEYBYTES];
|
||||
crypto_box_keypair(pk, sk);
|
||||
|
||||
const QString publicKey = getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES);
|
||||
const QString secretKey = getBase64FromKey(sk, crypto_box_SECRETKEYBYTES);
|
||||
if (publicKey.isEmpty() || secretKey.isEmpty()) {
|
||||
auto keyPair = browserMessageBuilder()->getKeyPair();
|
||||
if (keyPair.first.isEmpty() || keyPair.second.isEmpty()) {
|
||||
return getErrorReply(action, ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED);
|
||||
}
|
||||
|
||||
m_clientPublicKey = clientPublicKey;
|
||||
m_publicKey = publicKey;
|
||||
m_secretKey = secretKey;
|
||||
m_publicKey = keyPair.first;
|
||||
m_secretKey = keyPair.second;
|
||||
|
||||
QJsonObject response = buildMessage(incrementNonce(nonce));
|
||||
QJsonObject response = browserMessageBuilder()->buildMessage(browserMessageBuilder()->incrementNonce(nonce));
|
||||
response["action"] = action;
|
||||
response["publicKey"] = publicKey;
|
||||
response["publicKey"] = keyPair.first;
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -190,9 +142,9 @@ QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const
|
||||
|
||||
QString command = decrypted.value("action").toString();
|
||||
if (!command.isEmpty() && command.compare("get-databasehash") == 0) {
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["hash"] = hash;
|
||||
|
||||
// Update a legacy database hash if found
|
||||
@ -236,9 +188,9 @@ QJsonObject BrowserAction::handleAssociate(const QJsonObject& json, const QStrin
|
||||
}
|
||||
|
||||
m_associated = true;
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["hash"] = hash;
|
||||
message["id"] = id;
|
||||
return buildResponse(action, message, newNonce);
|
||||
@ -270,9 +222,9 @@ QJsonObject BrowserAction::handleTestAssociate(const QJsonObject& json, const QS
|
||||
}
|
||||
|
||||
m_associated = true;
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["hash"] = hash;
|
||||
message["id"] = id;
|
||||
|
||||
@ -317,9 +269,9 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
|
||||
return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND);
|
||||
}
|
||||
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["count"] = users.count();
|
||||
message["entries"] = users;
|
||||
message["hash"] = hash;
|
||||
@ -328,12 +280,33 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
|
||||
return buildResponse(action, message, newNonce);
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::handleGeneratePassword(const QJsonObject& json, const QString& action)
|
||||
QJsonObject BrowserAction::handleGeneratePassword(QLocalSocket* socket, const QJsonObject& json, const QString& action)
|
||||
{
|
||||
auto errorMessage = getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
|
||||
auto nonce = json.value("nonce").toString();
|
||||
auto incrementedNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
browserService()->showPasswordGenerator(errorMessage, nonce);
|
||||
const QString encrypted = json.value("message").toString();
|
||||
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
|
||||
if (decrypted.isEmpty()) {
|
||||
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
|
||||
}
|
||||
|
||||
auto requestId = decrypted.value("requestID").toString();
|
||||
|
||||
// Do not allow multiple requests from the same client
|
||||
if (browserService()->isPasswordGeneratorRequested()) {
|
||||
auto errorReply = getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
|
||||
|
||||
// Append requestID to the response if found
|
||||
if (!requestId.isEmpty()) {
|
||||
errorReply["requestID"] = requestId;
|
||||
}
|
||||
|
||||
return errorReply;
|
||||
}
|
||||
|
||||
browserService()->showPasswordGenerator(socket, incrementedNonce, m_clientPublicKey, m_secretKey);
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
@ -379,9 +352,9 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString
|
||||
result = browserService()->updateEntry(id, uuid, login, password, url, submitUrl);
|
||||
}
|
||||
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["count"] = QJsonValue::Null;
|
||||
message["entries"] = QJsonValue::Null;
|
||||
message["error"] = result ? QStringLiteral("success") : QStringLiteral("error");
|
||||
@ -409,8 +382,8 @@ QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QSt
|
||||
if (!command.isEmpty() && command.compare("lock-database") == 0) {
|
||||
browserService()->lockDatabase();
|
||||
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
|
||||
return buildResponse(action, message, newNonce);
|
||||
}
|
||||
@ -443,9 +416,9 @@ QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, cons
|
||||
return getErrorReply(action, ERROR_KEEPASS_NO_GROUPS_FOUND);
|
||||
}
|
||||
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["groups"] = groups;
|
||||
|
||||
return buildResponse(action, message, newNonce);
|
||||
@ -477,9 +450,9 @@ QJsonObject BrowserAction::handleCreateNewGroup(const QJsonObject& json, const Q
|
||||
return getErrorReply(action, ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP);
|
||||
}
|
||||
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["name"] = newGroup["name"];
|
||||
message["uuid"] = newGroup["uuid"];
|
||||
|
||||
@ -512,9 +485,9 @@ QJsonObject BrowserAction::handleGetTotp(const QJsonObject& json, const QString&
|
||||
|
||||
// Get the current TOTP
|
||||
const auto totp = browserService()->getCurrentTotp(uuid);
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["totp"] = totp;
|
||||
|
||||
return buildResponse(action, message, newNonce);
|
||||
@ -546,8 +519,8 @@ QJsonObject BrowserAction::handleDeleteEntry(const QJsonObject& json, const QStr
|
||||
|
||||
const auto result = browserService()->deleteEntry(uuid);
|
||||
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
message["success"] = result ? TRUE_STR : FALSE_STR;
|
||||
|
||||
return buildResponse(action, message, newNonce);
|
||||
@ -575,202 +548,22 @@ QJsonObject BrowserAction::handleGlobalAutoType(const QJsonObject& json, const Q
|
||||
|
||||
browserService()->requestGlobalAutoType(topLevelDomain);
|
||||
|
||||
const QString newNonce = incrementNonce(nonce);
|
||||
QJsonObject message = buildMessage(newNonce);
|
||||
const QString newNonce = browserMessageBuilder()->incrementNonce(nonce);
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(newNonce);
|
||||
return buildResponse(action, message, newNonce);
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorCode) const
|
||||
{
|
||||
QJsonObject response;
|
||||
response["action"] = action;
|
||||
response["errorCode"] = QString::number(errorCode);
|
||||
response["error"] = getErrorMessage(errorCode);
|
||||
return response;
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::buildMessage(const QString& nonce) const
|
||||
{
|
||||
QJsonObject message;
|
||||
message["version"] = KEEPASSXC_VERSION;
|
||||
message["success"] = TRUE_STR;
|
||||
message["nonce"] = nonce;
|
||||
return message;
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::buildResponse(const QString& action, const QJsonObject& message, const QString& nonce)
|
||||
{
|
||||
QJsonObject response;
|
||||
QString encryptedMessage = encryptMessage(message, nonce);
|
||||
if (encryptedMessage.isEmpty()) {
|
||||
return getErrorReply(action, ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE);
|
||||
}
|
||||
|
||||
response["action"] = action;
|
||||
response["message"] = encryptedMessage;
|
||||
response["nonce"] = nonce;
|
||||
return response;
|
||||
}
|
||||
|
||||
QString BrowserAction::getErrorMessage(const int errorCode) const
|
||||
{
|
||||
switch (errorCode) {
|
||||
case ERROR_KEEPASS_DATABASE_NOT_OPENED:
|
||||
return QObject::tr("Database not opened");
|
||||
case ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED:
|
||||
return QObject::tr("Database hash not available");
|
||||
case ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED:
|
||||
return QObject::tr("Client public key not received");
|
||||
case ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE:
|
||||
return QObject::tr("Cannot decrypt message");
|
||||
case ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED:
|
||||
return QObject::tr("Action cancelled or denied");
|
||||
case ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE:
|
||||
return QObject::tr("Message encryption failed.");
|
||||
case ERROR_KEEPASS_ASSOCIATION_FAILED:
|
||||
return QObject::tr("KeePassXC association failed, try again");
|
||||
case ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED:
|
||||
return QObject::tr("Encryption key is not recognized");
|
||||
case ERROR_KEEPASS_INCORRECT_ACTION:
|
||||
return QObject::tr("Incorrect action");
|
||||
case ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED:
|
||||
return QObject::tr("Empty message received");
|
||||
case ERROR_KEEPASS_NO_URL_PROVIDED:
|
||||
return QObject::tr("No URL provided");
|
||||
case ERROR_KEEPASS_NO_LOGINS_FOUND:
|
||||
return QObject::tr("No logins found");
|
||||
case ERROR_KEEPASS_NO_GROUPS_FOUND:
|
||||
return QObject::tr("No groups found");
|
||||
case ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP:
|
||||
return QObject::tr("Cannot create new group");
|
||||
case ERROR_KEEPASS_NO_VALID_UUID_PROVIDED:
|
||||
return QObject::tr("No valid UUID provided");
|
||||
default:
|
||||
return QObject::tr("Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
QString BrowserAction::encryptMessage(const QJsonObject& message, const QString& nonce)
|
||||
{
|
||||
if (message.isEmpty() || nonce.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
const QString reply(QJsonDocument(message).toJson());
|
||||
if (!reply.isEmpty()) {
|
||||
return encrypt(reply, nonce);
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::decryptMessage(const QString& message, const QString& nonce)
|
||||
{
|
||||
if (message.isEmpty() || nonce.isEmpty()) {
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
QByteArray ba = decrypt(message, nonce);
|
||||
if (ba.isEmpty()) {
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
return getJsonObject(ba);
|
||||
return browserMessageBuilder()->decryptMessage(message, nonce, m_clientPublicKey, m_secretKey);
|
||||
}
|
||||
|
||||
QString BrowserAction::encrypt(const QString& plaintext, const QString& nonce)
|
||||
QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorCode) const
|
||||
{
|
||||
const QByteArray ma = plaintext.toUtf8();
|
||||
const QByteArray na = base64Decode(nonce);
|
||||
const QByteArray ca = base64Decode(m_clientPublicKey);
|
||||
const QByteArray sa = base64Decode(m_secretKey);
|
||||
|
||||
std::vector<unsigned char> m(ma.cbegin(), ma.cend());
|
||||
std::vector<unsigned char> n(na.cbegin(), na.cend());
|
||||
std::vector<unsigned char> ck(ca.cbegin(), ca.cend());
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> e;
|
||||
e.resize(BrowserShared::NATIVEMSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (crypto_box_easy(e.data(), m.data(), m.size(), n.data(), ck.data(), sk.data()) == 0) {
|
||||
QByteArray res = getQByteArray(e.data(), (crypto_box_MACBYTES + ma.length()));
|
||||
return res.toBase64();
|
||||
}
|
||||
|
||||
return QString();
|
||||
return browserMessageBuilder()->getErrorReply(action, errorCode);
|
||||
}
|
||||
|
||||
QByteArray BrowserAction::decrypt(const QString& encrypted, const QString& nonce)
|
||||
QJsonObject BrowserAction::buildResponse(const QString& action, const QJsonObject& message, const QString& nonce)
|
||||
{
|
||||
const QByteArray ma = base64Decode(encrypted);
|
||||
const QByteArray na = base64Decode(nonce);
|
||||
const QByteArray ca = base64Decode(m_clientPublicKey);
|
||||
const QByteArray sa = base64Decode(m_secretKey);
|
||||
|
||||
std::vector<unsigned char> m(ma.cbegin(), ma.cend());
|
||||
std::vector<unsigned char> n(na.cbegin(), na.cend());
|
||||
std::vector<unsigned char> ck(ca.cbegin(), ca.cend());
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> d;
|
||||
d.resize(BrowserShared::NATIVEMSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
if (crypto_box_open_easy(d.data(), m.data(), ma.length(), n.data(), ck.data(), sk.data()) == 0) {
|
||||
return getQByteArray(d.data(), std::char_traits<char>::length(reinterpret_cast<const char*>(d.data())));
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QString BrowserAction::getBase64FromKey(const uchar* array, const uint len)
|
||||
{
|
||||
return getQByteArray(array, len).toBase64();
|
||||
}
|
||||
|
||||
QByteArray BrowserAction::getQByteArray(const uchar* array, const uint len) const
|
||||
{
|
||||
QByteArray qba;
|
||||
qba.reserve(len);
|
||||
for (uint i = 0; i < len; ++i) {
|
||||
qba.append(static_cast<char>(array[i]));
|
||||
}
|
||||
return qba;
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::getJsonObject(const uchar* pArray, const uint len) const
|
||||
{
|
||||
QByteArray arr = getQByteArray(pArray, len);
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(arr, &err));
|
||||
return doc.object();
|
||||
}
|
||||
|
||||
QJsonObject BrowserAction::getJsonObject(const QByteArray& ba) const
|
||||
{
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(ba, &err));
|
||||
return doc.object();
|
||||
}
|
||||
|
||||
QByteArray BrowserAction::base64Decode(const QString& str)
|
||||
{
|
||||
return QByteArray::fromBase64(str.toUtf8());
|
||||
}
|
||||
|
||||
QString BrowserAction::incrementNonce(const QString& nonce)
|
||||
{
|
||||
const QByteArray nonceArray = base64Decode(nonce);
|
||||
std::vector<unsigned char> n(nonceArray.cbegin(), nonceArray.cend());
|
||||
|
||||
sodium_increment(n.data(), n.size());
|
||||
return getQByteArray(n.data(), n.size()).toBase64();
|
||||
return browserMessageBuilder()->buildResponse(action, message, nonce, m_clientPublicKey, m_secretKey);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -21,23 +21,24 @@
|
||||
#include <QString>
|
||||
|
||||
class QJsonObject;
|
||||
class QLocalSocket;
|
||||
|
||||
class BrowserAction
|
||||
{
|
||||
public:
|
||||
explicit BrowserAction();
|
||||
explicit BrowserAction() = default;
|
||||
~BrowserAction() = default;
|
||||
|
||||
QJsonObject processClientMessage(const QJsonObject& json);
|
||||
QJsonObject processClientMessage(QLocalSocket* socket, const QJsonObject& json);
|
||||
|
||||
private:
|
||||
QJsonObject handleAction(const QJsonObject& json);
|
||||
QJsonObject handleAction(QLocalSocket* socket, const QJsonObject& json);
|
||||
QJsonObject handleChangePublicKeys(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleGetDatabaseHash(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleAssociate(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleTestAssociate(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleGetLogins(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleGeneratePassword(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleGeneratePassword(QLocalSocket* socket, const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleSetLogin(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleLockDatabase(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleGetDatabaseGroups(const QJsonObject& json, const QString& action);
|
||||
@ -46,22 +47,11 @@ private:
|
||||
QJsonObject handleDeleteEntry(const QJsonObject& json, const QString& action);
|
||||
QJsonObject handleGlobalAutoType(const QJsonObject& json, const QString& action);
|
||||
|
||||
private:
|
||||
QJsonObject buildMessage(const QString& nonce) const;
|
||||
QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce);
|
||||
QJsonObject getErrorReply(const QString& action, const int errorCode) const;
|
||||
QString getErrorMessage(const int errorCode) const;
|
||||
|
||||
QString encryptMessage(const QJsonObject& message, const QString& nonce);
|
||||
QJsonObject decryptMessage(const QString& message, const QString& nonce);
|
||||
QString encrypt(const QString& plaintext, const QString& nonce);
|
||||
QByteArray decrypt(const QString& encrypted, const QString& nonce);
|
||||
|
||||
QString getBase64FromKey(const uchar* array, const uint len);
|
||||
QByteArray getQByteArray(const uchar* array, const uint len) const;
|
||||
QJsonObject getJsonObject(const uchar* pArray, const uint len) const;
|
||||
QJsonObject getJsonObject(const QByteArray& ba) const;
|
||||
QByteArray base64Decode(const QString& str);
|
||||
QString incrementNonce(const QString& nonce);
|
||||
|
||||
private:
|
||||
static const int MaxUrlLength;
|
||||
|
@ -88,18 +88,29 @@ void BrowserHost::readProxyMessage()
|
||||
return;
|
||||
}
|
||||
|
||||
emit clientMessageReceived(json.object());
|
||||
emit clientMessageReceived(socket, json.object());
|
||||
}
|
||||
|
||||
void BrowserHost::sendClientMessage(const QJsonObject& json)
|
||||
void BrowserHost::broadcastClientMessage(const QJsonObject& json)
|
||||
{
|
||||
QString reply(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
for (const auto socket : m_socketList) {
|
||||
if (socket && socket->isValid() && socket->state() == QLocalSocket::ConnectedState) {
|
||||
QByteArray arr = reply.toUtf8();
|
||||
socket->write(arr.constData(), arr.length());
|
||||
socket->flush();
|
||||
}
|
||||
sendClientData(socket, reply);
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserHost::sendClientMessage(QLocalSocket* socket, const QJsonObject& json)
|
||||
{
|
||||
QString reply(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
sendClientData(socket, reply);
|
||||
}
|
||||
|
||||
void BrowserHost::sendClientData(QLocalSocket* socket, const QString& data)
|
||||
{
|
||||
if (socket && socket->isValid() && socket->state() == QLocalSocket::ConnectedState) {
|
||||
QByteArray arr = data.toUtf8();
|
||||
socket->write(arr.constData(), arr.length());
|
||||
socket->flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
class QLocalServer;
|
||||
class QLocalSocket;
|
||||
class QString;
|
||||
|
||||
class BrowserHost : public QObject
|
||||
{
|
||||
@ -36,16 +37,20 @@ public:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void sendClientMessage(const QJsonObject& json);
|
||||
void broadcastClientMessage(const QJsonObject& json);
|
||||
void sendClientMessage(QLocalSocket* socket, const QJsonObject& json);
|
||||
|
||||
signals:
|
||||
void clientMessageReceived(const QJsonObject& json);
|
||||
void clientMessageReceived(QLocalSocket* socket, const QJsonObject& json);
|
||||
|
||||
private slots:
|
||||
void proxyConnected();
|
||||
void readProxyMessage();
|
||||
void proxyDisconnected();
|
||||
|
||||
private:
|
||||
void sendClientData(QLocalSocket* socket, const QString& data);
|
||||
|
||||
private:
|
||||
QPointer<QLocalServer> m_localServer;
|
||||
QList<QLocalSocket*> m_socketList;
|
||||
|
259
src/browser/BrowserMessageBuilder.cpp
Normal file
259
src/browser/BrowserMessageBuilder.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "BrowserMessageBuilder.h"
|
||||
#include "BrowserShared.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Global.h"
|
||||
#include "core/Tools.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <botan/sodium.h>
|
||||
|
||||
using namespace Botan::Sodium;
|
||||
|
||||
Q_GLOBAL_STATIC(BrowserMessageBuilder, s_browserMessageBuilder);
|
||||
|
||||
BrowserMessageBuilder* BrowserMessageBuilder::instance()
|
||||
{
|
||||
return s_browserMessageBuilder;
|
||||
}
|
||||
|
||||
QPair<QString, QString> BrowserMessageBuilder::getKeyPair()
|
||||
{
|
||||
unsigned char pk[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_box_SECRETKEYBYTES];
|
||||
crypto_box_keypair(pk, sk);
|
||||
|
||||
const QString publicKey = getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES);
|
||||
const QString secretKey = getBase64FromKey(sk, crypto_box_SECRETKEYBYTES);
|
||||
return qMakePair(publicKey, secretKey);
|
||||
}
|
||||
|
||||
QJsonObject BrowserMessageBuilder::getErrorReply(const QString& action, const int errorCode) const
|
||||
{
|
||||
QJsonObject response;
|
||||
response["action"] = action;
|
||||
response["errorCode"] = QString::number(errorCode);
|
||||
response["error"] = getErrorMessage(errorCode);
|
||||
return response;
|
||||
}
|
||||
|
||||
QJsonObject BrowserMessageBuilder::buildMessage(const QString& nonce) const
|
||||
{
|
||||
QJsonObject message;
|
||||
message["version"] = KEEPASSXC_VERSION;
|
||||
message["success"] = TRUE_STR;
|
||||
message["nonce"] = nonce;
|
||||
return message;
|
||||
}
|
||||
|
||||
QJsonObject BrowserMessageBuilder::buildResponse(const QString& action,
|
||||
const QJsonObject& message,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey)
|
||||
{
|
||||
QJsonObject response;
|
||||
QString encryptedMessage = encryptMessage(message, nonce, publicKey, secretKey);
|
||||
if (encryptedMessage.isEmpty()) {
|
||||
return getErrorReply(action, ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE);
|
||||
}
|
||||
|
||||
response["action"] = action;
|
||||
response["message"] = encryptedMessage;
|
||||
response["nonce"] = nonce;
|
||||
return response;
|
||||
}
|
||||
|
||||
QString BrowserMessageBuilder::getErrorMessage(const int errorCode) const
|
||||
{
|
||||
switch (errorCode) {
|
||||
case ERROR_KEEPASS_DATABASE_NOT_OPENED:
|
||||
return QObject::tr("Database not opened");
|
||||
case ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED:
|
||||
return QObject::tr("Database hash not available");
|
||||
case ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED:
|
||||
return QObject::tr("Client public key not received");
|
||||
case ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE:
|
||||
return QObject::tr("Cannot decrypt message");
|
||||
case ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED:
|
||||
return QObject::tr("Action cancelled or denied");
|
||||
case ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE:
|
||||
return QObject::tr("Message encryption failed.");
|
||||
case ERROR_KEEPASS_ASSOCIATION_FAILED:
|
||||
return QObject::tr("KeePassXC association failed, try again");
|
||||
case ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED:
|
||||
return QObject::tr("Encryption key is not recognized");
|
||||
case ERROR_KEEPASS_INCORRECT_ACTION:
|
||||
return QObject::tr("Incorrect action");
|
||||
case ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED:
|
||||
return QObject::tr("Empty message received");
|
||||
case ERROR_KEEPASS_NO_URL_PROVIDED:
|
||||
return QObject::tr("No URL provided");
|
||||
case ERROR_KEEPASS_NO_LOGINS_FOUND:
|
||||
return QObject::tr("No logins found");
|
||||
case ERROR_KEEPASS_NO_GROUPS_FOUND:
|
||||
return QObject::tr("No groups found");
|
||||
case ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP:
|
||||
return QObject::tr("Cannot create new group");
|
||||
case ERROR_KEEPASS_NO_VALID_UUID_PROVIDED:
|
||||
return QObject::tr("No valid UUID provided");
|
||||
default:
|
||||
return QObject::tr("Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
QString BrowserMessageBuilder::encryptMessage(const QJsonObject& message,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey)
|
||||
{
|
||||
if (message.isEmpty() || nonce.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
const QString reply(QJsonDocument(message).toJson());
|
||||
if (!reply.isEmpty()) {
|
||||
return encrypt(reply, nonce, publicKey, secretKey);
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QJsonObject BrowserMessageBuilder::decryptMessage(const QString& message,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey)
|
||||
{
|
||||
if (message.isEmpty() || nonce.isEmpty()) {
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
QByteArray ba = decrypt(message, nonce, publicKey, secretKey);
|
||||
if (ba.isEmpty()) {
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
return getJsonObject(ba);
|
||||
}
|
||||
|
||||
QString BrowserMessageBuilder::encrypt(const QString& plaintext,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey)
|
||||
{
|
||||
const QByteArray ma = plaintext.toUtf8();
|
||||
const QByteArray na = base64Decode(nonce);
|
||||
const QByteArray ca = base64Decode(publicKey);
|
||||
const QByteArray sa = base64Decode(secretKey);
|
||||
|
||||
std::vector<unsigned char> m(ma.cbegin(), ma.cend());
|
||||
std::vector<unsigned char> n(na.cbegin(), na.cend());
|
||||
std::vector<unsigned char> ck(ca.cbegin(), ca.cend());
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> e;
|
||||
e.resize(BrowserShared::NATIVEMSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (crypto_box_easy(e.data(), m.data(), m.size(), n.data(), ck.data(), sk.data()) == 0) {
|
||||
QByteArray res = getQByteArray(e.data(), (crypto_box_MACBYTES + ma.length()));
|
||||
return res.toBase64();
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QByteArray BrowserMessageBuilder::decrypt(const QString& encrypted,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey)
|
||||
{
|
||||
const QByteArray ma = base64Decode(encrypted);
|
||||
const QByteArray na = base64Decode(nonce);
|
||||
const QByteArray ca = base64Decode(publicKey);
|
||||
const QByteArray sa = base64Decode(secretKey);
|
||||
|
||||
std::vector<unsigned char> m(ma.cbegin(), ma.cend());
|
||||
std::vector<unsigned char> n(na.cbegin(), na.cend());
|
||||
std::vector<unsigned char> ck(ca.cbegin(), ca.cend());
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> d;
|
||||
d.resize(BrowserShared::NATIVEMSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
if (crypto_box_open_easy(d.data(), m.data(), ma.length(), n.data(), ck.data(), sk.data()) == 0) {
|
||||
return getQByteArray(d.data(), std::char_traits<char>::length(reinterpret_cast<const char*>(d.data())));
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QString BrowserMessageBuilder::getBase64FromKey(const uchar* array, const uint len)
|
||||
{
|
||||
return getQByteArray(array, len).toBase64();
|
||||
}
|
||||
|
||||
QByteArray BrowserMessageBuilder::getQByteArray(const uchar* array, const uint len) const
|
||||
{
|
||||
QByteArray qba;
|
||||
qba.reserve(len);
|
||||
for (uint i = 0; i < len; ++i) {
|
||||
qba.append(static_cast<char>(array[i]));
|
||||
}
|
||||
return qba;
|
||||
}
|
||||
|
||||
QJsonObject BrowserMessageBuilder::getJsonObject(const uchar* pArray, const uint len) const
|
||||
{
|
||||
QByteArray arr = getQByteArray(pArray, len);
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(arr, &err));
|
||||
return doc.object();
|
||||
}
|
||||
|
||||
QJsonObject BrowserMessageBuilder::getJsonObject(const QByteArray& ba) const
|
||||
{
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(ba, &err));
|
||||
return doc.object();
|
||||
}
|
||||
|
||||
QByteArray BrowserMessageBuilder::base64Decode(const QString& str)
|
||||
{
|
||||
return QByteArray::fromBase64(str.toUtf8());
|
||||
}
|
||||
|
||||
QString BrowserMessageBuilder::incrementNonce(const QString& nonce)
|
||||
{
|
||||
const QByteArray nonceArray = base64Decode(nonce);
|
||||
std::vector<unsigned char> n(nonceArray.cbegin(), nonceArray.cend());
|
||||
|
||||
sodium_increment(n.data(), n.size());
|
||||
return getQByteArray(n.data(), n.size()).toBase64();
|
||||
}
|
96
src/browser/BrowserMessageBuilder.h
Normal file
96
src/browser/BrowserMessageBuilder.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BROWSERMESSAGEBUILDER_H
|
||||
#define BROWSERMESSAGEBUILDER_H
|
||||
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
ERROR_KEEPASS_DATABASE_NOT_OPENED = 1,
|
||||
ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED = 2,
|
||||
ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED = 3,
|
||||
ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE = 4,
|
||||
ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED = 5,
|
||||
ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED = 6,
|
||||
ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE = 7,
|
||||
ERROR_KEEPASS_ASSOCIATION_FAILED = 8,
|
||||
ERROR_KEEPASS_KEY_CHANGE_FAILED = 9,
|
||||
ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED = 10,
|
||||
ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND = 11,
|
||||
ERROR_KEEPASS_INCORRECT_ACTION = 12,
|
||||
ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED = 13,
|
||||
ERROR_KEEPASS_NO_URL_PROVIDED = 14,
|
||||
ERROR_KEEPASS_NO_LOGINS_FOUND = 15,
|
||||
ERROR_KEEPASS_NO_GROUPS_FOUND = 16,
|
||||
ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17,
|
||||
ERROR_KEEPASS_NO_VALID_UUID_PROVIDED = 18
|
||||
};
|
||||
}
|
||||
|
||||
class BrowserMessageBuilder
|
||||
{
|
||||
public:
|
||||
explicit BrowserMessageBuilder() = default;
|
||||
static BrowserMessageBuilder* instance();
|
||||
|
||||
QPair<QString, QString> getKeyPair();
|
||||
|
||||
QJsonObject buildMessage(const QString& nonce) const;
|
||||
QJsonObject buildResponse(const QString& action,
|
||||
const QJsonObject& message,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey);
|
||||
QJsonObject getErrorReply(const QString& action, const int errorCode) const;
|
||||
QString getErrorMessage(const int errorCode) const;
|
||||
|
||||
QString encryptMessage(const QJsonObject& message,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey);
|
||||
QJsonObject
|
||||
decryptMessage(const QString& message, const QString& nonce, const QString& publicKey, const QString& secretKey);
|
||||
QString encrypt(const QString& plaintext, const QString& nonce, const QString& publicKey, const QString& secretKey);
|
||||
QByteArray
|
||||
decrypt(const QString& encrypted, const QString& nonce, const QString& publicKey, const QString& secretKey);
|
||||
|
||||
QString getBase64FromKey(const uchar* array, const uint len);
|
||||
QByteArray getQByteArray(const uchar* array, const uint len) const;
|
||||
QJsonObject getJsonObject(const uchar* pArray, const uint len) const;
|
||||
QJsonObject getJsonObject(const QByteArray& ba) const;
|
||||
QByteArray base64Decode(const QString& str);
|
||||
QString incrementNonce(const QString& nonce);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(BrowserMessageBuilder);
|
||||
|
||||
friend class TestBrowser;
|
||||
};
|
||||
|
||||
static inline BrowserMessageBuilder* browserMessageBuilder()
|
||||
{
|
||||
return BrowserMessageBuilder::instance();
|
||||
}
|
||||
|
||||
#endif // BROWSERMESSAGEBUILDER_H
|
@ -23,6 +23,7 @@
|
||||
#include "BrowserEntryConfig.h"
|
||||
#include "BrowserEntrySaveDialog.h"
|
||||
#include "BrowserHost.h"
|
||||
#include "BrowserMessageBuilder.h"
|
||||
#include "BrowserSettings.h"
|
||||
#include "core/Tools.h"
|
||||
#include "gui/MainWindow.h"
|
||||
@ -39,6 +40,7 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QListWidget>
|
||||
#include <QLocalSocket>
|
||||
#include <QProgressDialog>
|
||||
#include <QUrl>
|
||||
|
||||
@ -64,6 +66,7 @@ BrowserService::BrowserService()
|
||||
, m_browserHost(new BrowserHost)
|
||||
, m_dialogActive(false)
|
||||
, m_bringToFrontRequested(false)
|
||||
, m_passwordGeneratorRequested(false)
|
||||
, m_prevWindowState(WindowState::Normal)
|
||||
, m_keepassBrowserUUID(Tools::hexToUuid("de887cc3036343b8974b5911b8816224"))
|
||||
{
|
||||
@ -311,37 +314,55 @@ QString BrowserService::getCurrentTotp(const QString& uuid)
|
||||
return {};
|
||||
}
|
||||
|
||||
void BrowserService::showPasswordGenerator(const QJsonObject& errorMessage, const QString& nonce)
|
||||
void BrowserService::showPasswordGenerator(QLocalSocket* socket,
|
||||
const QString& incrementedNonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey)
|
||||
{
|
||||
if (!m_passwordGenerator) {
|
||||
m_passwordGenerator.reset(PasswordGeneratorWidget::popupGenerator());
|
||||
|
||||
connect(m_passwordGenerator.data(), &PasswordGeneratorWidget::closed, m_passwordGenerator.data(), [=] {
|
||||
if (!m_passwordGenerator->isPasswordGenerated()) {
|
||||
m_browserHost->sendClientMessage(errorMessage);
|
||||
auto errorMessage = browserMessageBuilder()->getErrorReply("generate-password",
|
||||
ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
|
||||
m_browserHost->sendClientMessage(socket, errorMessage);
|
||||
}
|
||||
|
||||
m_passwordGenerator.reset();
|
||||
hideWindow();
|
||||
m_passwordGeneratorRequested = false;
|
||||
});
|
||||
|
||||
connect(m_passwordGenerator.data(),
|
||||
&PasswordGeneratorWidget::appliedPassword,
|
||||
m_passwordGenerator.data(),
|
||||
[=](const QString& password) { emit passwordGenerated(password, nonce); });
|
||||
[=](const QString& password) {
|
||||
QJsonObject message = browserMessageBuilder()->buildMessage(incrementedNonce);
|
||||
message["password"] = password;
|
||||
sendPassword(socket,
|
||||
browserMessageBuilder()->buildResponse(
|
||||
"generate-password", message, incrementedNonce, publicKey, secretKey));
|
||||
});
|
||||
}
|
||||
|
||||
m_passwordGeneratorRequested = true;
|
||||
raiseWindow();
|
||||
m_passwordGenerator->raise();
|
||||
m_passwordGenerator->activateWindow();
|
||||
}
|
||||
|
||||
void BrowserService::sendPassword(const QJsonObject& message)
|
||||
void BrowserService::sendPassword(QLocalSocket* socket, const QJsonObject& message)
|
||||
{
|
||||
m_browserHost->sendClientMessage(message);
|
||||
m_browserHost->sendClientMessage(socket, message);
|
||||
hideWindow();
|
||||
}
|
||||
|
||||
bool BrowserService::isPasswordGeneratorRequested() const
|
||||
{
|
||||
return m_passwordGeneratorRequested;
|
||||
}
|
||||
|
||||
QString BrowserService::storeKey(const QString& key)
|
||||
{
|
||||
auto db = getDatabase();
|
||||
@ -1382,7 +1403,7 @@ void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
|
||||
if (dbWidget) {
|
||||
QJsonObject msg;
|
||||
msg["action"] = QString("database-locked");
|
||||
m_browserHost->sendClientMessage(msg);
|
||||
m_browserHost->broadcastClientMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1396,7 +1417,7 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget)
|
||||
|
||||
QJsonObject msg;
|
||||
msg["action"] = QString("database-unlocked");
|
||||
m_browserHost->sendClientMessage(msg);
|
||||
m_browserHost->broadcastClientMessage(msg);
|
||||
|
||||
auto db = dbWidget->database();
|
||||
if (checkLegacySettings(db)) {
|
||||
@ -1419,7 +1440,7 @@ void BrowserService::activeDatabaseChanged(DatabaseWidget* dbWidget)
|
||||
m_currentDatabaseWidget = dbWidget;
|
||||
}
|
||||
|
||||
void BrowserService::processClientMessage(const QJsonObject& message)
|
||||
void BrowserService::processClientMessage(QLocalSocket* socket, const QJsonObject& message)
|
||||
{
|
||||
auto clientID = message["clientID"].toString();
|
||||
if (clientID.isEmpty()) {
|
||||
@ -1432,6 +1453,6 @@ void BrowserService::processClientMessage(const QJsonObject& message)
|
||||
}
|
||||
|
||||
auto& action = m_browserClients.value(clientID);
|
||||
auto response = action->processClientMessage(message);
|
||||
m_browserHost->sendClientMessage(response);
|
||||
auto response = action->processClientMessage(socket, message);
|
||||
m_browserHost->sendClientMessage(socket, response);
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "core/Entry.h"
|
||||
#include "gui/PasswordGeneratorWidget.h"
|
||||
|
||||
class QLocalSocket;
|
||||
|
||||
typedef QPair<QString, QString> StringPair;
|
||||
typedef QList<StringPair> StringPairList;
|
||||
|
||||
@ -56,8 +58,12 @@ public:
|
||||
QJsonObject getDatabaseGroups();
|
||||
QJsonObject createNewGroup(const QString& groupName);
|
||||
QString getCurrentTotp(const QString& uuid);
|
||||
void showPasswordGenerator(const QJsonObject& errorMessage, const QString& nonce);
|
||||
void sendPassword(const QJsonObject& message);
|
||||
void showPasswordGenerator(QLocalSocket* socket,
|
||||
const QString& nonce,
|
||||
const QString& publicKey,
|
||||
const QString& secretKey);
|
||||
void sendPassword(QLocalSocket* socket, const QJsonObject& message);
|
||||
bool isPasswordGeneratorRequested() const;
|
||||
|
||||
void addEntry(const QString& dbid,
|
||||
const QString& login,
|
||||
@ -97,7 +103,7 @@ public:
|
||||
|
||||
signals:
|
||||
void requestUnlock();
|
||||
void passwordGenerated(const QString& password, const QString& nonce);
|
||||
void passwordGenerated(QLocalSocket* socket, const QString& password, const QString& nonce);
|
||||
|
||||
public slots:
|
||||
void databaseLocked(DatabaseWidget* dbWidget);
|
||||
@ -105,7 +111,7 @@ public slots:
|
||||
void activeDatabaseChanged(DatabaseWidget* dbWidget);
|
||||
|
||||
private slots:
|
||||
void processClientMessage(const QJsonObject& message);
|
||||
void processClientMessage(QLocalSocket* socket, const QJsonObject& message);
|
||||
|
||||
private:
|
||||
enum Access
|
||||
@ -163,6 +169,7 @@ private:
|
||||
|
||||
bool m_dialogActive;
|
||||
bool m_bringToFrontRequested;
|
||||
bool m_passwordGeneratorRequested;
|
||||
WindowState m_prevWindowState;
|
||||
QUuid m_keepassBrowserUUID;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
||||
# Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
# Copyright (C) 2022 KeePassXC Team <team@keepassxc.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -23,6 +23,7 @@ if(WITH_XC_BROWSER)
|
||||
BrowserEntryConfig.cpp
|
||||
BrowserEntrySaveDialog.cpp
|
||||
BrowserHost.cpp
|
||||
BrowserMessageBuilder.cpp
|
||||
BrowserSettingsPage.cpp
|
||||
BrowserSettingsWidget.cpp
|
||||
BrowserService.cpp
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "TestBrowser.h"
|
||||
|
||||
#include "browser/BrowserMessageBuilder.h"
|
||||
#include "browser/BrowserSettings.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Tools.h"
|
||||
@ -61,7 +62,7 @@ void TestBrowser::testChangePublicKeys()
|
||||
json["publicKey"] = PUBLICKEY;
|
||||
json["nonce"] = NONCE;
|
||||
|
||||
auto response = m_browserAction->processClientMessage(json);
|
||||
auto response = m_browserAction->processClientMessage(nullptr, json);
|
||||
QCOMPARE(response["action"].toString(), QString("change-public-keys"));
|
||||
QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false);
|
||||
QCOMPARE(response["success"].toString(), TRUE_STR);
|
||||
@ -75,7 +76,7 @@ void TestBrowser::testEncryptMessage()
|
||||
m_browserAction->m_publicKey = SERVERPUBLICKEY;
|
||||
m_browserAction->m_secretKey = SERVERSECRETKEY;
|
||||
m_browserAction->m_clientPublicKey = PUBLICKEY;
|
||||
auto encrypted = m_browserAction->encryptMessage(message, NONCE);
|
||||
auto encrypted = browserMessageBuilder()->encryptMessage(message, NONCE, PUBLICKEY, SERVERSECRETKEY);
|
||||
|
||||
QCOMPARE(encrypted, QString("+zjtntnk4rGWSl/Ph7Vqip/swvgeupk4lNgHEm2OO3ujNr0OMz6eQtGwjtsj+/rP"));
|
||||
}
|
||||
@ -86,7 +87,7 @@ void TestBrowser::testDecryptMessage()
|
||||
m_browserAction->m_publicKey = SERVERPUBLICKEY;
|
||||
m_browserAction->m_secretKey = SERVERSECRETKEY;
|
||||
m_browserAction->m_clientPublicKey = PUBLICKEY;
|
||||
auto decrypted = m_browserAction->decryptMessage(message, NONCE);
|
||||
auto decrypted = browserMessageBuilder()->decryptMessage(message, NONCE, PUBLICKEY, SERVERSECRETKEY);
|
||||
|
||||
QCOMPARE(decrypted["action"].toString(), QString("test-action"));
|
||||
}
|
||||
@ -99,13 +100,13 @@ void TestBrowser::testGetBase64FromKey()
|
||||
pk[i] = i;
|
||||
}
|
||||
|
||||
auto response = m_browserAction->getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES);
|
||||
auto response = browserMessageBuilder()->getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES);
|
||||
QCOMPARE(response, QString("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="));
|
||||
}
|
||||
|
||||
void TestBrowser::testIncrementNonce()
|
||||
{
|
||||
auto result = m_browserAction->incrementNonce(NONCE);
|
||||
auto result = browserMessageBuilder()->incrementNonce(NONCE);
|
||||
QCOMPARE(result, QString("zRKdvTjL5bgWaKMCTut/8soM/uoMrFoZ"));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user