feat: port fingerprint info into Qt
mv FingerprintData -> Fingerprint
This commit is contained in:
committed by
Chris Rizzitello
parent
c4704649ea
commit
0309d35aef
@ -151,8 +151,8 @@ MainWindow::MainWindow()
|
||||
return;
|
||||
}
|
||||
|
||||
deskflow::FingerprintDatabase db;
|
||||
db.read(Settings::tlsLocalDb().toStdString());
|
||||
FingerprintDatabase db;
|
||||
db.read(Settings::tlsLocalDb());
|
||||
if (db.fingerprints().size() != kTlsDbSize) {
|
||||
regenerateLocalFingerprints();
|
||||
}
|
||||
@ -541,19 +541,15 @@ void MainWindow::showMyFingerprint()
|
||||
return;
|
||||
}
|
||||
|
||||
deskflow::FingerprintDatabase db;
|
||||
db.read(Settings::tlsLocalDb().toStdString());
|
||||
FingerprintDatabase db;
|
||||
db.read(Settings::tlsLocalDb());
|
||||
if (db.fingerprints().size() != kTlsDbSize) {
|
||||
if (regenerateLocalFingerprints())
|
||||
showMyFingerprint();
|
||||
return;
|
||||
}
|
||||
|
||||
QList<deskflow::FingerprintData> fingerprints;
|
||||
fingerprints.reserve(db.fingerprints().size());
|
||||
std::copy(db.fingerprints().begin(), db.fingerprints().end(), std::back_inserter(fingerprints));
|
||||
|
||||
FingerprintDialog fingerprintDialog(this, fingerprints);
|
||||
FingerprintDialog fingerprintDialog(this, db.fingerprints());
|
||||
fingerprintDialog.exec();
|
||||
}
|
||||
|
||||
@ -772,17 +768,11 @@ void MainWindow::checkFingerprint(const QString &line)
|
||||
return;
|
||||
}
|
||||
|
||||
const deskflow::FingerprintData sha1 = {
|
||||
deskflow::fingerprintTypeToString(deskflow::FingerprintType::SHA1),
|
||||
deskflow::string::fromHex(match.captured(1).toStdString())
|
||||
};
|
||||
const Fingerprint sha1 = {Fingerprint::Type::SHA1, QByteArray::fromHex(match.captured(1).remove(":").toLatin1())};
|
||||
|
||||
const auto sha256Text = match.captured(2);
|
||||
|
||||
const deskflow::FingerprintData sha256 = {
|
||||
deskflow::fingerprintTypeToString(deskflow::FingerprintType::SHA256),
|
||||
deskflow::string::fromHex(sha256Text.toStdString())
|
||||
};
|
||||
const Fingerprint sha256 = {Fingerprint::Type::SHA256, QByteArray::fromHex(match.captured(2).remove(":").toLatin1())};
|
||||
|
||||
const bool isClient = m_coreProcess.mode() == CoreMode::Client;
|
||||
|
||||
@ -791,8 +781,8 @@ void MainWindow::checkFingerprint(const QString &line)
|
||||
return;
|
||||
}
|
||||
|
||||
deskflow::FingerprintDatabase db;
|
||||
db.read(trustedFingerprintDb().toStdString());
|
||||
FingerprintDatabase db;
|
||||
db.read(trustedFingerprintDatabase());
|
||||
|
||||
if (db.isTrusted(sha256)) {
|
||||
qDebug("fingerprint is trusted");
|
||||
@ -813,7 +803,7 @@ void MainWindow::checkFingerprint(const QString &line)
|
||||
|
||||
if (fingerprintDialog.exec() == QDialog::Accepted) {
|
||||
db.addTrusted(sha256);
|
||||
db.write(trustedFingerprintDb().toStdString());
|
||||
db.write(trustedFingerprintDatabase());
|
||||
if (isClient) {
|
||||
m_checkedServers.removeAll(sha256Text);
|
||||
m_coreProcess.start();
|
||||
@ -1116,7 +1106,7 @@ void MainWindow::setHostName()
|
||||
applyConfig();
|
||||
}
|
||||
|
||||
QString MainWindow::trustedFingerprintDb()
|
||||
QString MainWindow::trustedFingerprintDatabase()
|
||||
{
|
||||
const bool isClient = m_coreProcess.mode() == CoreMode::Client;
|
||||
return isClient ? Settings::tlsTrustedServersDb() : Settings::tlsTrustedClientsDb();
|
||||
|
||||
@ -156,10 +156,10 @@ private:
|
||||
void remoteHostChanged(const QString &newRemoteHost);
|
||||
|
||||
/**
|
||||
* @brief trustedFingerprintDb get the fingerprintDb for the trusted clients or trusted servers.
|
||||
* @brief trustedFingerprintDatabase get the FingerprintDatabase for the trusted clients or trusted servers.
|
||||
* @return The path to the trusted fingerprint file
|
||||
*/
|
||||
QString trustedFingerprintDb();
|
||||
QString trustedFingerprintDatabase();
|
||||
|
||||
// Generate prints if they are missing
|
||||
// Returns true if successful
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
FingerprintDialog::FingerprintDialog(
|
||||
QWidget *parent, const QList<deskflow::FingerprintData> &fingerprints, FingerprintDialogMode mode
|
||||
QWidget *parent, const QList<Fingerprint> &fingerprints, FingerprintDialogMode mode
|
||||
)
|
||||
: QDialog(parent),
|
||||
m_lblHeader{new QLabel(this)},
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net/FingerprintData.h"
|
||||
#include "net/Fingerprint.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
@ -27,7 +27,7 @@ class FingerprintDialog : public QDialog
|
||||
|
||||
public:
|
||||
explicit FingerprintDialog(
|
||||
QWidget *parent = nullptr, const QList<deskflow::FingerprintData> &fingerprints = {},
|
||||
QWidget *parent = nullptr, const QList<Fingerprint> &fingerprints = {},
|
||||
FingerprintDialogMode mode = FingerprintDialogMode::Local
|
||||
);
|
||||
~FingerprintDialog() override = default;
|
||||
|
||||
@ -9,7 +9,8 @@
|
||||
|
||||
#include "base/FinalAction.h"
|
||||
#include "common/Settings.h"
|
||||
#include "net/FingerprintData.h"
|
||||
#include "io/Filesystem.h"
|
||||
#include "net/Fingerprint.h"
|
||||
#include "net/FingerprintDatabase.h"
|
||||
#include "net/SecureUtils.h"
|
||||
|
||||
@ -52,10 +53,10 @@ bool TlsCertificate::generateFingerprint(const QString &certificateFilename)
|
||||
qDebug("generating tls fingerprint");
|
||||
const std::string certPath = certificateFilename.toStdString();
|
||||
try {
|
||||
deskflow::FingerprintDatabase db;
|
||||
db.addTrusted(deskflow::pemFileCertFingerprint(certPath, deskflow::FingerprintType::SHA1));
|
||||
db.addTrusted(deskflow::pemFileCertFingerprint(certPath, deskflow::FingerprintType::SHA256));
|
||||
db.write(Settings::tlsLocalDb().toStdString());
|
||||
FingerprintDatabase db;
|
||||
db.addTrusted(deskflow::pemFileCertFingerprint(certPath, Fingerprint::Type::SHA1));
|
||||
db.addTrusted(deskflow::pemFileCertFingerprint(certPath, Fingerprint::Type::SHA256));
|
||||
db.write(Settings::tlsLocalDb());
|
||||
|
||||
qDebug("tls fingerprint generated");
|
||||
return true;
|
||||
|
||||
@ -12,8 +12,7 @@
|
||||
|
||||
#include <net/SecureUtils.h>
|
||||
|
||||
FingerprintPreview::FingerprintPreview(QWidget *parent, const QList<deskflow::FingerprintData> &fingerprints)
|
||||
: QFrame(parent)
|
||||
FingerprintPreview::FingerprintPreview(QWidget *parent, const QList<Fingerprint> &fingerprints) : QFrame(parent)
|
||||
{
|
||||
setFrameShape(QFrame::StyledPanel);
|
||||
setFrameStyle(QFrame::Sunken);
|
||||
@ -24,13 +23,18 @@ FingerprintPreview::FingerprintPreview(QWidget *parent, const QList<deskflow::Fi
|
||||
QString sha256Art;
|
||||
|
||||
for (const auto &fingerprint : fingerprints) {
|
||||
if (fingerprint.algorithm == "sha1") {
|
||||
sha1String = QString::fromStdString(deskflow::formatSSLFingerprint(fingerprint.data));
|
||||
}
|
||||
switch (fingerprint.type) {
|
||||
case Fingerprint::Type::SHA1:
|
||||
sha1String = deskflow::formatSSLFingerprint(fingerprint.data);
|
||||
break;
|
||||
|
||||
if (fingerprint.algorithm == "sha256") {
|
||||
sha256String = QString::fromStdString(deskflow::formatSSLFingerprintColumns(fingerprint.data));
|
||||
sha256Art = QString::fromStdString(deskflow::generateFingerprintArt(fingerprint.data));
|
||||
case Fingerprint::Type::SHA256:
|
||||
sha256String = deskflow::formatSSLFingerprintColumns(fingerprint.data);
|
||||
sha256Art = deskflow::generateFingerprintArt(fingerprint.data);
|
||||
break;
|
||||
default:
|
||||
qWarning() << "FingerprintPreview: Unknown fingerprint type: " << fingerprint.type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <net/FingerprintData.h>
|
||||
#include <net/Fingerprint.h>
|
||||
|
||||
class FingerprintPreview : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FingerprintPreview(QWidget *parent, const QList<deskflow::FingerprintData> &fingerprints = {});
|
||||
explicit FingerprintPreview(QWidget *parent, const QList<Fingerprint> &fingerprints = {});
|
||||
~FingerprintPreview() override = default;
|
||||
};
|
||||
|
||||
@ -13,8 +13,8 @@ endif()
|
||||
find_package(OpenSSL ${REQUIRED_OPENSSL_VERSION} REQUIRED COMPONENTS SSL Crypto)
|
||||
|
||||
add_library(net STATIC
|
||||
FingerprintData.cpp
|
||||
FingerprintData.h
|
||||
Fingerprint.cpp
|
||||
Fingerprint.h
|
||||
FingerprintDatabase.cpp
|
||||
FingerprintDatabase.h
|
||||
IDataSocket.cpp
|
||||
|
||||
88
src/lib/net/Fingerprint.cpp
Normal file
88
src/lib/net/Fingerprint.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-FileCopyrightText: (C) 2021 Barrier Contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "Fingerprint.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
bool Fingerprint::isValid() const
|
||||
{
|
||||
switch (type) {
|
||||
case Fingerprint::Type::Invalid:
|
||||
return false;
|
||||
case Fingerprint::Type::SHA1:
|
||||
return data.length() == 20;
|
||||
case Fingerprint::Type::SHA256:
|
||||
return data.length() == 32;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Fingerprint::operator==(const Fingerprint &other) const
|
||||
{
|
||||
return type == other.type && data == other.data;
|
||||
}
|
||||
|
||||
QString Fingerprint::toDbLine() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
qInfo() << "unable to parse invalid db line";
|
||||
return {};
|
||||
}
|
||||
return QStringLiteral("v2:%1:%2").arg(typeToString(type), data.toHex().toLower());
|
||||
}
|
||||
|
||||
Fingerprint Fingerprint::fromDbLine(const QString &line)
|
||||
{
|
||||
Fingerprint result;
|
||||
|
||||
if (line.isEmpty())
|
||||
return result;
|
||||
|
||||
if (line.startsWith("v2:")) {
|
||||
const auto sLine = line.split(':');
|
||||
if (sLine.count() != 3)
|
||||
return result;
|
||||
result.type = typeFromString(sLine.at(1));
|
||||
result.data = QByteArray::fromHex(sLine.at(2).toLower().toLatin1());
|
||||
} else {
|
||||
// v1 fallback
|
||||
const auto kSha1ColonCount = 19;
|
||||
const auto kSha1HexCharCount = 40;
|
||||
const auto kSha1ExpectedSize = kSha1HexCharCount + kSha1ColonCount;
|
||||
if (line.size() != kSha1ExpectedSize || line.count(':') != kSha1ColonCount)
|
||||
return result;
|
||||
result.type = Fingerprint::Type::SHA1;
|
||||
auto l2 = line;
|
||||
result.data = QByteArray::fromHex(l2.remove(':').toLatin1());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Fingerprint::Type Fingerprint::typeFromString(const QString &type)
|
||||
{
|
||||
const auto t = type.toLower();
|
||||
if (t == m_type_sha1)
|
||||
return Type::SHA1;
|
||||
if (t == m_type_sha256)
|
||||
return Type::SHA256;
|
||||
return Type::Invalid;
|
||||
}
|
||||
|
||||
QString Fingerprint::typeToString(Fingerprint::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case Type::SHA1:
|
||||
return m_type_sha1;
|
||||
case Type::SHA256:
|
||||
return m_type_sha256;
|
||||
default:
|
||||
return m_type_invalid;
|
||||
}
|
||||
}
|
||||
38
src/lib/net/Fingerprint.h
Normal file
38
src/lib/net/Fingerprint.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
|
||||
struct Fingerprint
|
||||
{
|
||||
private:
|
||||
Q_GADGET
|
||||
inline static QString m_type_sha1 = QStringLiteral("sha1");
|
||||
inline static QString m_type_sha256 = QStringLiteral("sha256");
|
||||
inline static QString m_type_invalid = QStringLiteral("invalid");
|
||||
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
Invalid,
|
||||
SHA1,
|
||||
SHA256
|
||||
};
|
||||
Q_ENUM(Type)
|
||||
Type type = Type::Invalid;
|
||||
QByteArray data;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool operator==(const Fingerprint &other) const;
|
||||
QString toDbLine() const;
|
||||
static Fingerprint fromDbLine(const QString &line);
|
||||
static QString typeToString(Fingerprint::Type type);
|
||||
static Fingerprint::Type typeFromString(const QString &type);
|
||||
};
|
||||
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-FileCopyrightText: (C) 2021 Barrier Contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "FingerprintDatabase.h"
|
||||
|
||||
#include "io/Filesystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
namespace deskflow {
|
||||
|
||||
bool FingerprintData::operator==(const FingerprintData &other) const
|
||||
{
|
||||
return algorithm == other.algorithm && data == other.data;
|
||||
}
|
||||
|
||||
const char *fingerprintTypeToString(FingerprintType type)
|
||||
{
|
||||
switch (type) {
|
||||
case FingerprintType::Invalid:
|
||||
return "invalid";
|
||||
case FingerprintType::SHA1:
|
||||
return "sha1";
|
||||
case FingerprintType::SHA256:
|
||||
return "sha256";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
FingerprintType fingerprintTypeFromString(const std::string &type)
|
||||
{
|
||||
if (type == "sha1")
|
||||
return FingerprintType::SHA1;
|
||||
|
||||
if (type == "sha256")
|
||||
return FingerprintType::SHA256;
|
||||
|
||||
return FingerprintType::Invalid;
|
||||
}
|
||||
|
||||
} // namespace deskflow
|
||||
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
||||
* SPDX-FileCopyrightText: (C) 2021 Barrier Contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace deskflow {
|
||||
|
||||
enum FingerprintType
|
||||
{
|
||||
Invalid,
|
||||
SHA1,
|
||||
SHA256
|
||||
};
|
||||
|
||||
struct FingerprintData
|
||||
{
|
||||
std::string algorithm;
|
||||
std::vector<std::uint8_t> data;
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return !algorithm.empty();
|
||||
}
|
||||
|
||||
bool operator==(const FingerprintData &other) const;
|
||||
};
|
||||
|
||||
const char *fingerprintTypeToString(FingerprintType type);
|
||||
FingerprintType fingerprintTypeFromString(const std::string &type);
|
||||
|
||||
} // namespace deskflow
|
||||
@ -7,57 +7,69 @@
|
||||
|
||||
#include "FingerprintDatabase.h"
|
||||
|
||||
#include "base/String.h"
|
||||
#include "io/Filesystem.h"
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
namespace deskflow {
|
||||
|
||||
void FingerprintDatabase::read(const fs::path &path)
|
||||
void FingerprintDatabase::read(const QString &path)
|
||||
{
|
||||
std::ifstream file;
|
||||
openUtf8Path(file, path, std::ios_base::in);
|
||||
readStream(file);
|
||||
}
|
||||
|
||||
void FingerprintDatabase::write(const fs::path &path)
|
||||
{
|
||||
std::ofstream file;
|
||||
openUtf8Path(file, path, std::ios_base::out);
|
||||
writeStream(file);
|
||||
}
|
||||
|
||||
void FingerprintDatabase::readStream(std::istream &stream)
|
||||
{
|
||||
if (!stream.good()) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
QTextStream in(&file);
|
||||
readStream(in);
|
||||
}
|
||||
|
||||
void FingerprintDatabase::readStream(QTextStream &in)
|
||||
{
|
||||
// Make sure the stream has something to read
|
||||
if (!in.device() && !in.string())
|
||||
return;
|
||||
|
||||
if (in.device()) {
|
||||
if (!in.device()->isReadable())
|
||||
return;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(stream, line)) {
|
||||
if (line.empty()) {
|
||||
if (in.string()) {
|
||||
if (in.string()->isEmpty())
|
||||
return;
|
||||
}
|
||||
|
||||
QString line;
|
||||
while (!in.atEnd()) {
|
||||
line = in.readLine();
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
auto fingerprint = Fingerprint::fromDbLine(line);
|
||||
if (!fingerprint.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto fingerprint = parseDbLine(line);
|
||||
if (!fingerprint.valid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_fingerprints.push_back(fingerprint);
|
||||
m_fingerprints.append(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
void FingerprintDatabase::writeStream(std::ostream &stream)
|
||||
void FingerprintDatabase::write(const QString &path)
|
||||
{
|
||||
if (!stream.good()) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
return;
|
||||
QTextStream out(&file);
|
||||
writeStream(out);
|
||||
}
|
||||
|
||||
void FingerprintDatabase::writeStream(QTextStream &out)
|
||||
{
|
||||
// Make sure the stream has somewhere to write
|
||||
if (!out.device() && !out.string())
|
||||
return;
|
||||
|
||||
if (out.device()) {
|
||||
if (!out.device()->isWritable())
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &fingerprint : m_fingerprints) {
|
||||
stream << toDbLine(fingerprint) << "\n";
|
||||
for (const auto &fingerprint : std::as_const(m_fingerprints)) {
|
||||
out << fingerprint.toDbLine() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,68 +78,15 @@ void FingerprintDatabase::clear()
|
||||
m_fingerprints.clear();
|
||||
}
|
||||
|
||||
void FingerprintDatabase::addTrusted(const FingerprintData &fingerprint)
|
||||
void FingerprintDatabase::addTrusted(const Fingerprint &fingerprint)
|
||||
{
|
||||
if (isTrusted(fingerprint)) {
|
||||
return;
|
||||
}
|
||||
m_fingerprints.push_back(fingerprint);
|
||||
m_fingerprints.append(fingerprint);
|
||||
}
|
||||
|
||||
bool FingerprintDatabase::isTrusted(const FingerprintData &fingerprint)
|
||||
bool FingerprintDatabase::isTrusted(const Fingerprint &fingerprint)
|
||||
{
|
||||
auto found = std::find(m_fingerprints.begin(), m_fingerprints.end(), fingerprint);
|
||||
return found != m_fingerprints.end();
|
||||
return m_fingerprints.contains(fingerprint);
|
||||
}
|
||||
|
||||
FingerprintData FingerprintDatabase::parseDbLine(const std::string &line)
|
||||
{
|
||||
|
||||
const auto kSha1ColonCount = 19;
|
||||
const auto kSha1HexCharCount = 40;
|
||||
const auto kSha1ExpectedSize = kSha1HexCharCount + kSha1ColonCount;
|
||||
|
||||
FingerprintData result;
|
||||
|
||||
// legacy v1 certificate handling
|
||||
if (std::count(line.begin(), line.end(), ':') == kSha1ColonCount && line.size() == kSha1ExpectedSize) {
|
||||
auto data = string::fromHex(line);
|
||||
if (data.empty()) {
|
||||
return result;
|
||||
}
|
||||
result.algorithm = fingerprintTypeToString(FingerprintType::SHA1);
|
||||
result.data = data;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto versionEndPos = line.find(':');
|
||||
if (versionEndPos == std::string::npos) {
|
||||
return result;
|
||||
}
|
||||
if (line.substr(0, versionEndPos) != "v2") {
|
||||
return result;
|
||||
}
|
||||
auto algoStartPos = versionEndPos + 1;
|
||||
auto algoEndPos = line.find(':', algoStartPos);
|
||||
if (algoEndPos == std::string::npos) {
|
||||
return result;
|
||||
}
|
||||
auto algorithm = line.substr(algoStartPos, algoEndPos - algoStartPos);
|
||||
auto data = string::fromHex(line.substr(algoEndPos + 1));
|
||||
|
||||
if (data.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.algorithm = algorithm;
|
||||
result.data = data;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string FingerprintDatabase::toDbLine(const FingerprintData &fingerprint)
|
||||
{
|
||||
std::string fingerprintStr(fingerprint.data.begin(), fingerprint.data.end());
|
||||
return "v2:" + fingerprint.algorithm + ":" + string::toHex(fingerprintStr, 2);
|
||||
}
|
||||
|
||||
} // namespace deskflow
|
||||
|
||||
@ -7,39 +7,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FingerprintData.h"
|
||||
#include "Fingerprint.h"
|
||||
|
||||
#include "io/Filesystem.h"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace deskflow {
|
||||
#include <QList>
|
||||
|
||||
class FingerprintDatabase
|
||||
{
|
||||
public:
|
||||
void read(const fs::path &path);
|
||||
void write(const fs::path &path);
|
||||
void read(const QString &path);
|
||||
void write(const QString &path);
|
||||
|
||||
void readStream(std::istream &stream);
|
||||
void writeStream(std::ostream &stream);
|
||||
void readStream(QTextStream &in);
|
||||
void writeStream(QTextStream &out);
|
||||
|
||||
void clear();
|
||||
void addTrusted(const FingerprintData &fingerprint);
|
||||
bool isTrusted(const FingerprintData &fingerprint);
|
||||
void addTrusted(const Fingerprint &fingerprint);
|
||||
bool isTrusted(const Fingerprint &fingerprint);
|
||||
|
||||
const std::vector<FingerprintData> &fingerprints() const
|
||||
const QList<Fingerprint> &fingerprints() const
|
||||
{
|
||||
return m_fingerprints;
|
||||
}
|
||||
|
||||
static FingerprintData parseDbLine(const std::string &line);
|
||||
static std::string toDbLine(const FingerprintData &fingerprint);
|
||||
|
||||
private:
|
||||
std::vector<FingerprintData> m_fingerprints;
|
||||
QList<Fingerprint> m_fingerprints;
|
||||
};
|
||||
|
||||
} // namespace deskflow
|
||||
|
||||
@ -448,8 +448,7 @@ int SecureSocket::secureAccept(int socket)
|
||||
// If not fatal and no retry, state is good
|
||||
if (retry == 0) {
|
||||
if (m_securityLevel == SecurityLevel::PeerAuth) {
|
||||
std::string dbDir = Settings::tlsTrustedClientsDb().toStdString().c_str();
|
||||
if (!verifyCertFingerprint(dbDir)) {
|
||||
if (!verifyCertFingerprint(Settings::tlsTrustedClientsDb())) {
|
||||
retry = 0;
|
||||
disconnect();
|
||||
return -1; // Fail
|
||||
@ -521,8 +520,7 @@ int SecureSocket::secureConnect(int socket)
|
||||
retry = 0;
|
||||
// No error, set ready, process and return ok
|
||||
m_secureReady = true;
|
||||
std::string dbDir = Settings::tlsTrustedServersDb().toStdString().c_str();
|
||||
if (verifyCertFingerprint(dbDir)) {
|
||||
if (verifyCertFingerprint(Settings::tlsTrustedServersDb())) {
|
||||
LOG((CLOG_INFO "connected to secure socket"));
|
||||
if (!showCertificate()) {
|
||||
disconnect();
|
||||
@ -645,14 +643,14 @@ void SecureSocket::disconnect()
|
||||
sendEvent(EventTypes::StreamInputShutdown);
|
||||
}
|
||||
|
||||
bool SecureSocket::verifyCertFingerprint(const deskflow::fs::path &fingerprintDbPath)
|
||||
bool SecureSocket::verifyCertFingerprint(const QString &FingerprintDatabasePath)
|
||||
{
|
||||
deskflow::FingerprintData sha1;
|
||||
deskflow::FingerprintData sha256;
|
||||
Fingerprint sha1;
|
||||
Fingerprint sha256;
|
||||
try {
|
||||
auto cert = SSL_get_peer_certificate(m_ssl->m_ssl);
|
||||
sha1 = deskflow::sslCertFingerprint(cert, deskflow::FingerprintType::SHA1);
|
||||
sha256 = deskflow::sslCertFingerprint(cert, deskflow::FingerprintType::SHA256);
|
||||
sha1 = deskflow::sslCertFingerprint(cert, Fingerprint::Type::SHA1);
|
||||
sha256 = deskflow::sslCertFingerprint(cert, Fingerprint::Type::SHA256);
|
||||
} catch (const std::exception &e) {
|
||||
LOG((CLOG_ERR "%s", e.what()));
|
||||
return false;
|
||||
@ -660,26 +658,25 @@ bool SecureSocket::verifyCertFingerprint(const deskflow::fs::path &fingerprintDb
|
||||
|
||||
// Gui Must Parse these two lines, DO NOT CHANGE
|
||||
LOG(
|
||||
(CLOG_NOTE "peer fingerprint: (SHA1) %s (SHA256) %s", deskflow::formatSSLFingerprint(sha1.data).c_str(),
|
||||
deskflow::formatSSLFingerprint(sha256.data).c_str())
|
||||
(CLOG_NOTE "peer fingerprint: (SHA1) %s (SHA256) %s",
|
||||
deskflow::formatSSLFingerprint(sha1.data).toStdString().c_str(),
|
||||
deskflow::formatSSLFingerprint(sha256.data).toStdString().c_str())
|
||||
);
|
||||
|
||||
// check if this fingerprint exist
|
||||
std::ifstream file;
|
||||
QFile file(FingerprintDatabasePath);
|
||||
|
||||
deskflow::openUtf8Path(file, fingerprintDbPath);
|
||||
deskflow::FingerprintDatabase db;
|
||||
db.read(fingerprintDbPath);
|
||||
FingerprintDatabase db;
|
||||
db.read(FingerprintDatabasePath);
|
||||
const bool emptyDB = db.fingerprints().empty();
|
||||
|
||||
const auto &path = fingerprintDbPath.string();
|
||||
if (file.good() && emptyDB) {
|
||||
LOG((CLOG_ERR "failed to open trusted fingerprints file: %s", path.c_str()));
|
||||
const auto &path = FingerprintDatabasePath;
|
||||
if (file.exists() && emptyDB) {
|
||||
LOG((CLOG_ERR "failed to open trusted fingerprints file: %s", path.toStdString().c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emptyDB) {
|
||||
LOG((CLOG_DEBUG "read %d fingerprint(s) from file: %s", db.fingerprints().size(), path.c_str()));
|
||||
LOG((CLOG_DEBUG "read %d fingerprint(s) from file: %s", db.fingerprints().size(), path.toStdString().c_str()));
|
||||
}
|
||||
|
||||
if (!db.isTrusted(sha256)) {
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
class IEventQueue;
|
||||
class SocketMultiplexer;
|
||||
class ISocketMultiplexerJob;
|
||||
class QString;
|
||||
|
||||
struct Ssl;
|
||||
|
||||
@ -76,7 +77,7 @@ private:
|
||||
bool showCertificate() const;
|
||||
void checkResult(int n, int &retry);
|
||||
void disconnect();
|
||||
bool verifyCertFingerprint(const deskflow::fs::path &fingerprintDbPath);
|
||||
bool verifyCertFingerprint(const QString &FingerprintDatabasePath);
|
||||
|
||||
ISocketMultiplexerJob *serviceConnect(ISocketMultiplexerJob *, bool, bool, bool);
|
||||
|
||||
|
||||
@ -23,12 +23,12 @@ namespace deskflow {
|
||||
|
||||
namespace {
|
||||
|
||||
const EVP_MD *digestForType(FingerprintType type)
|
||||
const EVP_MD *digestForType(Fingerprint::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case FingerprintType::SHA1:
|
||||
case Fingerprint::Type::SHA1:
|
||||
return EVP_sha1();
|
||||
case FingerprintType::SHA256:
|
||||
case Fingerprint::Type::SHA256:
|
||||
return EVP_sha256();
|
||||
default:
|
||||
break;
|
||||
@ -38,22 +38,15 @@ const EVP_MD *digestForType(FingerprintType type)
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string formatSSLFingerprint(const std::vector<uint8_t> &fingerprint, bool enableSeparators)
|
||||
QString formatSSLFingerprint(const QByteArray &fingerprint, bool enableSeparators)
|
||||
{
|
||||
std::string result = deskflow::string::toHex(fingerprint, 2);
|
||||
|
||||
deskflow::string::uppercase(result);
|
||||
|
||||
if (enableSeparators) {
|
||||
const auto usedSpaces = 3;
|
||||
size_t separators = result.size() / 2;
|
||||
for (size_t i = 1; i < separators; i++)
|
||||
result.insert(i * usedSpaces - 1, ":");
|
||||
}
|
||||
return result;
|
||||
if (enableSeparators)
|
||||
return fingerprint.toHex(':').toUpper();
|
||||
else
|
||||
return fingerprint.toHex().toUpper();
|
||||
}
|
||||
|
||||
FingerprintData sslCertFingerprint(X509 *cert, FingerprintType type)
|
||||
Fingerprint sslCertFingerprint(X509 *cert, Fingerprint::Type type)
|
||||
{
|
||||
if (!cert) {
|
||||
throw std::runtime_error("certificate is null");
|
||||
@ -67,12 +60,11 @@ FingerprintData sslCertFingerprint(X509 *cert, FingerprintType type)
|
||||
throw std::runtime_error("failed to calculate fingerprint, digest result: " + std::to_string(result));
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> digestVec;
|
||||
digestVec.assign(reinterpret_cast<std::uint8_t *>(digest), reinterpret_cast<std::uint8_t *>(digest) + digestLength);
|
||||
return {fingerprintTypeToString(type), digestVec};
|
||||
QByteArray digestArray(reinterpret_cast<const char *>(digest), digestLength);
|
||||
return {type, digestArray};
|
||||
}
|
||||
|
||||
FingerprintData pemFileCertFingerprint(const std::string &path, FingerprintType type)
|
||||
Fingerprint pemFileCertFingerprint(const std::string &path, Fingerprint::Type type)
|
||||
{
|
||||
auto fp = fopenUtf8Path(path, "r");
|
||||
if (!fp) {
|
||||
@ -156,27 +148,25 @@ int getCertLength(const std::string &path)
|
||||
return size;
|
||||
}
|
||||
|
||||
std::string formatSSLFingerprintColumns(const std::vector<uint8_t> &fingerprint)
|
||||
QString formatSSLFingerprintColumns(const QByteArray &fingerprint)
|
||||
{
|
||||
auto kmaxColumns = 8;
|
||||
auto kmaxColumns = 24;
|
||||
|
||||
std::string hex = deskflow::string::toHex(fingerprint, 2);
|
||||
deskflow::string::uppercase(hex);
|
||||
if (hex.empty() || hex.size() % 2 != 0) {
|
||||
QString hex = fingerprint.toHex(':').toUpper();
|
||||
if (hex.isEmpty()) {
|
||||
return hex;
|
||||
}
|
||||
|
||||
std::string separated;
|
||||
for (std::size_t i = 0; i < hex.size(); i += kmaxColumns * 2) {
|
||||
for (std::size_t j = i; j < i + 16 && j < hex.size() - 1; j += 2) {
|
||||
separated.push_back(hex[j]);
|
||||
separated.push_back(hex[j + 1]);
|
||||
separated.push_back(':');
|
||||
}
|
||||
separated.push_back('\n');
|
||||
QString final;
|
||||
while (!hex.isEmpty()) {
|
||||
final.append(hex.mid(0, kmaxColumns));
|
||||
hex.remove(0, kmaxColumns);
|
||||
if (final.endsWith(':'))
|
||||
final.removeLast();
|
||||
final.append('\n');
|
||||
}
|
||||
separated.pop_back(); // we don't need last newline character
|
||||
return separated;
|
||||
final.removeLast();
|
||||
return final;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -206,12 +196,12 @@ walked in either direction.
|
||||
fail, too, because the key type would not fit in anymore.
|
||||
*/
|
||||
|
||||
std::string generateFingerprintArt(const std::vector<std::uint8_t> &rawDigest)
|
||||
QString generateFingerprintArt(const QByteArray &rawDigest)
|
||||
{
|
||||
const auto baseSize = 8;
|
||||
const auto rows = (baseSize + 1);
|
||||
const auto columns = (baseSize * 2 + 1);
|
||||
const std::string characterPool = " .o+=*BOX@%&#/^SE";
|
||||
const QString characterPool = " .o+=*BOX@%&#/^SE";
|
||||
const std::size_t len = characterPool.length() - 1;
|
||||
|
||||
std::uint8_t field[columns][rows];
|
||||
@ -243,7 +233,7 @@ std::string generateFingerprintArt(const std::vector<std::uint8_t> &rawDigest)
|
||||
field[columns / 2][rows / 2] = len - 1;
|
||||
field[x][y] = len;
|
||||
|
||||
std::string result;
|
||||
QString result;
|
||||
result.reserve((columns + 3) * (rows + 2));
|
||||
result.append("╔═════════════════╗\n");
|
||||
|
||||
@ -251,7 +241,7 @@ std::string generateFingerprintArt(const std::vector<std::uint8_t> &rawDigest)
|
||||
for (y = 0; y < rows; y++) {
|
||||
result.append("║");
|
||||
for (x = 0; x < columns; x++)
|
||||
result.append(characterPool.substr(std::min<int>(field[x][y], len), 1));
|
||||
result.append(characterPool.at(std::min<int>(field[x][y], len)));
|
||||
result.append("║\n");
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FingerprintData.h"
|
||||
#include "Fingerprint.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <openssl/ossl_typ.h>
|
||||
@ -22,17 +22,17 @@ namespace deskflow {
|
||||
* @param enableSeparators insert : seperator every byte when true
|
||||
* @return a Formated Fingerprint String
|
||||
*/
|
||||
std::string formatSSLFingerprint(const std::vector<uint8_t> &fingerprint, bool enableSeparators = true);
|
||||
QString formatSSLFingerprint(const QByteArray &fingerprint, bool enableSeparators = true);
|
||||
|
||||
std::string formatSSLFingerprintColumns(const std::vector<uint8_t> &fingerprint);
|
||||
QString formatSSLFingerprintColumns(const QByteArray &fingerprint);
|
||||
|
||||
FingerprintData sslCertFingerprint(X509 *cert, FingerprintType type);
|
||||
Fingerprint sslCertFingerprint(X509 *cert, Fingerprint::Type type);
|
||||
|
||||
FingerprintData pemFileCertFingerprint(const std::string &path, FingerprintType type);
|
||||
Fingerprint pemFileCertFingerprint(const std::string &path, Fingerprint::Type type);
|
||||
|
||||
void generatePemSelfSignedCert(const std::string &path, int keyLength = 2048);
|
||||
|
||||
int getCertLength(const std::string &path);
|
||||
|
||||
std::string generateFingerprintArt(const std::vector<std::uint8_t> &rawDigest);
|
||||
QString generateFingerprintArt(const QByteArray &rawDigest);
|
||||
} // namespace deskflow
|
||||
|
||||
@ -13,6 +13,14 @@ create_test(
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/net"
|
||||
)
|
||||
|
||||
create_test(
|
||||
NAME FingerprintTests
|
||||
DEPENDS net
|
||||
LIBS base arch mt io ${extra_libs}
|
||||
SOURCE FingerprintTests.cpp
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/net"
|
||||
)
|
||||
|
||||
create_test(
|
||||
NAME FingerprintDatabaseTests
|
||||
DEPENDS net
|
||||
|
||||
@ -7,39 +7,28 @@
|
||||
|
||||
#include "FingerprintDatabaseTests.h"
|
||||
|
||||
#include "net/FingerprintData.h"
|
||||
#include "net/Fingerprint.h"
|
||||
#include "net/FingerprintDatabase.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace deskflow;
|
||||
|
||||
void FingerprintDatabaseTests::parseDBLine()
|
||||
{
|
||||
QVERIFY(!FingerprintDatabase::parseDbLine("").valid());
|
||||
QVERIFY(!FingerprintDatabase::parseDbLine("abcd").valid());
|
||||
QVERIFY(!FingerprintDatabase::parseDbLine("v1:algo:something").valid());
|
||||
QVERIFY(!FingerprintDatabase::parseDbLine("v2:algo:something").valid());
|
||||
QVERIFY(!FingerprintDatabase::parseDbLine("v2:algo:01020304abc").valid());
|
||||
QVERIFY(!FingerprintDatabase::parseDbLine("v2:algo:01020304ZZ").valid());
|
||||
QCOMPARE(FingerprintDatabase::parseDbLine("v2:algo:01020304ab"), (FingerprintData{"algo", {1, 2, 3, 4, 0xab}}));
|
||||
}
|
||||
|
||||
void FingerprintDatabaseTests::readFile()
|
||||
{
|
||||
std::istringstream stream;
|
||||
stream.str(R"(
|
||||
v2:algo1:01020304ab
|
||||
v2:algo2:03040506ab
|
||||
QString data = R"(
|
||||
v2:sha1:01020304ab
|
||||
v2:sha1:03040506ab
|
||||
AB:CD:EF:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16
|
||||
)");
|
||||
)";
|
||||
|
||||
QTextStream stream(&data);
|
||||
|
||||
FingerprintDatabase db;
|
||||
db.readStream(stream);
|
||||
|
||||
std::vector<FingerprintData> expected = {
|
||||
{"algo1", {1, 2, 3, 4, 0xab}},
|
||||
{"algo2", {3, 4, 5, 6, 0xab}},
|
||||
{"sha1", {0xab, 0xcd, 0xef, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
|
||||
// Only one will be in our list as only one is valid
|
||||
QList<Fingerprint> expected = {
|
||||
{Fingerprint::Type::SHA1,
|
||||
QByteArray::fromRawData("\xAB\xCD\xEF\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16", 20)}
|
||||
};
|
||||
|
||||
QCOMPARE(db.fingerprints(), expected);
|
||||
@ -47,22 +36,27 @@ AB:CD:EF:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16
|
||||
|
||||
void FingerprintDatabaseTests::writeFile()
|
||||
{
|
||||
std::ostringstream stream;
|
||||
QString out;
|
||||
QTextStream stream(&out);
|
||||
|
||||
FingerprintDatabase db;
|
||||
db.addTrusted({"algo1", {1, 2, 3, 4, 0xab}});
|
||||
db.addTrusted({"algo2", {3, 4, 5, 6, 0xab}});
|
||||
db.addTrusted(
|
||||
{Fingerprint::Type::SHA1, QByteArray::fromHex(QString("ABCDEF0001020304050607080910111213141516").toLatin1())}
|
||||
);
|
||||
db.addTrusted(
|
||||
{Fingerprint::Type::SHA1, QByteArray::fromHex(QString("0001020304050607080910111213141516ABCDEF").toLatin1())}
|
||||
);
|
||||
db.writeStream(stream);
|
||||
|
||||
QCOMPARE(stream.str(), R"(v2:algo1:01020304ab
|
||||
v2:algo2:03040506ab
|
||||
QCOMPARE(stream.readAll(), R"(v2:sha1:abcdef0001020304050607080910111213141516
|
||||
v2:sha1:0001020304050607080910111213141516abcdef
|
||||
)");
|
||||
}
|
||||
|
||||
void FingerprintDatabaseTests::clear()
|
||||
{
|
||||
FingerprintDatabase db;
|
||||
db.addTrusted({"algo1", {1, 2, 3, 4, 0xab}});
|
||||
db.addTrusted({Fingerprint::Type::SHA1, QByteArray::fromHex(QString("01020304ab").toLatin1())});
|
||||
db.clear();
|
||||
|
||||
QVERIFY(db.fingerprints().empty());
|
||||
@ -70,17 +64,22 @@ void FingerprintDatabaseTests::clear()
|
||||
|
||||
void FingerprintDatabaseTests::trusted()
|
||||
{
|
||||
Fingerprint trusted1 = {Fingerprint::Type::SHA1, QByteArray::fromHex(QString("01020304ab").toLatin1())};
|
||||
Fingerprint trusted2 = {Fingerprint::Type::SHA1, QByteArray::fromHex(QString("03040506ab").toLatin1())};
|
||||
Fingerprint untrusted = {Fingerprint::Type::SHA1, QByteArray::fromHex(QString("01020304ac").toLatin1())};
|
||||
|
||||
FingerprintDatabase db;
|
||||
|
||||
db.addTrusted({"algo1", {1, 2, 3, 4, 0xab}});
|
||||
QVERIFY(db.isTrusted({"algo1", {1, 2, 3, 4, 0xab}}));
|
||||
QVERIFY(!db.isTrusted({"algo2", {1, 2, 3, 4, 0xab}}));
|
||||
QVERIFY(!db.isTrusted({"algo1", {1, 2, 3, 4, 0xac}}));
|
||||
db.addTrusted(trusted1);
|
||||
QVERIFY(db.isTrusted(trusted1));
|
||||
QVERIFY(!db.isTrusted(trusted2));
|
||||
QVERIFY(!db.isTrusted(untrusted));
|
||||
|
||||
db.addTrusted({"algo2", {3, 4, 5, 6, 0xab}});
|
||||
QVERIFY(db.isTrusted({"algo2", {3, 4, 5, 6, 0xab}}));
|
||||
db.addTrusted(trusted2);
|
||||
QVERIFY(db.isTrusted(trusted1));
|
||||
QVERIFY(db.isTrusted(trusted2));
|
||||
QVERIFY(!db.isTrusted(untrusted));
|
||||
|
||||
db.addTrusted({"algo1", {1, 2, 3, 4, 0xab}});
|
||||
QCOMPARE(db.fingerprints().size(), 2);
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ class FingerprintDatabaseTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void parseDBLine();
|
||||
void readFile();
|
||||
void writeFile();
|
||||
void clear();
|
||||
|
||||
188
src/unittests/net/FingerprintTests.cpp
Normal file
188
src/unittests/net/FingerprintTests.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "FingerprintTests.h"
|
||||
|
||||
#include "net/Fingerprint.h"
|
||||
|
||||
void FingerprintTests::test_isValid()
|
||||
{
|
||||
Fingerprint f;
|
||||
|
||||
QVERIFY(!f.isValid());
|
||||
|
||||
// Always invalid without a type
|
||||
f.data = f.data.fill('\x23', 1);
|
||||
QVERIFY(!f.isValid());
|
||||
|
||||
// SHA1 Tests
|
||||
f.type = Fingerprint::Type::SHA1;
|
||||
|
||||
// Invalid SHA1, no Data
|
||||
f.data.clear();
|
||||
QVERIFY(!f.isValid());
|
||||
|
||||
// Invalid SHA1, 5 bytes
|
||||
f.data = f.data.fill('\x23', 5);
|
||||
QVERIFY(!f.isValid());
|
||||
|
||||
// Valid SHA1, 20 bytes
|
||||
f.data = f.data.fill('\x23', 20);
|
||||
QVERIFY(f.isValid());
|
||||
|
||||
// Invalid SHA1, 25 bytes
|
||||
f.data = f.data.fill('\x23', 25);
|
||||
QVERIFY(!f.isValid());
|
||||
|
||||
// SHA256 Tests
|
||||
f.type = Fingerprint::Type::SHA256;
|
||||
|
||||
// Invalid SHA256, no Data
|
||||
f.data.clear();
|
||||
QVERIFY(!f.isValid());
|
||||
|
||||
// Invalid SHA256, 16 bytes
|
||||
f.data = f.data.fill('\x23', 16);
|
||||
QVERIFY(!f.isValid());
|
||||
|
||||
// Valid SHA256, 32 bytes
|
||||
f.data = f.data.fill('\x23', 32);
|
||||
QVERIFY(f.isValid());
|
||||
|
||||
// Invalid SHA256, 50 bytes
|
||||
f.data = f.data.fill('\x23', 50);
|
||||
QVERIFY(!f.isValid());
|
||||
}
|
||||
|
||||
void FingerprintTests::test_toDbLine()
|
||||
{
|
||||
Fingerprint f;
|
||||
|
||||
// Invalid Fingerprints return empty string
|
||||
QVERIFY(f.toDbLine().isEmpty());
|
||||
|
||||
// Always invalid without a type
|
||||
f.data = f.data.fill('\x23', 20);
|
||||
QVERIFY(f.toDbLine().isEmpty());
|
||||
|
||||
// Invalid SHA1, type w/o data
|
||||
f.type = Fingerprint::Type::SHA1;
|
||||
f.data.clear();
|
||||
QVERIFY(f.toDbLine().isEmpty());
|
||||
|
||||
// Valid Sha1
|
||||
f.data = f.data.fill('\x23', 20);
|
||||
auto expectedString = QStringLiteral("v2:sha1:2323232323232323232323232323232323232323");
|
||||
QCOMPARE(f.toDbLine(), expectedString);
|
||||
|
||||
// Valid Sha256
|
||||
f.type = Fingerprint::Type::SHA256;
|
||||
f.data = f.data.fill('\x23', 32);
|
||||
expectedString = QStringLiteral("v2:sha256:2323232323232323232323232323232323232323232323232323232323232323");
|
||||
QCOMPARE(f.toDbLine(), expectedString);
|
||||
}
|
||||
|
||||
void FingerprintTests::test_fromDbLine()
|
||||
{
|
||||
Fingerprint expected;
|
||||
|
||||
// The Following are all invalid
|
||||
auto actual = Fingerprint::fromDbLine("");
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
actual = Fingerprint::fromDbLine("abcd");
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
actual = Fingerprint::fromDbLine("v1:algo:something");
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
actual = Fingerprint::fromDbLine("v2:algo:something");
|
||||
expected.data = QByteArray::fromHex(QString("something").toLatin1());
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
actual = Fingerprint::fromDbLine("v2:algo:01020304abc");
|
||||
expected.data = QByteArray::fromHex(QString("01020304abc").toLatin1());
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
actual = Fingerprint::fromDbLine("v2:algo:01020304ZZ");
|
||||
expected.data = QByteArray::fromHex(QString("01020304ZZ").toLatin1());
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
// Test V1 Only support Sha1
|
||||
expected.type = Fingerprint::Type::SHA1;
|
||||
expected.data =
|
||||
QByteArray::fromRawData("\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23", 20);
|
||||
actual = Fingerprint::fromDbLine("23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23");
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
// V1 does not support SHA256
|
||||
expected.type = Fingerprint::Type::SHA256;
|
||||
expected.data = QByteArray::fromRawData(
|
||||
"\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23"
|
||||
"\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23",
|
||||
32
|
||||
);
|
||||
actual = Fingerprint::fromDbLine(
|
||||
"23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23:23"
|
||||
);
|
||||
QCOMPARE_NE(actual, expected);
|
||||
|
||||
// V2 SHA1 Test
|
||||
expected.type = Fingerprint::Type::SHA1;
|
||||
expected.data =
|
||||
QByteArray::fromRawData("\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23", 20);
|
||||
actual = Fingerprint::fromDbLine("v2:sha1:2323232323232323232323232323232323232323");
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
// V2 SHA1 Invalid Input
|
||||
actual = Fingerprint::fromDbLine("v2:sha1:23232323232323232323232323232323232323");
|
||||
QCOMPARE_NE(actual, expected);
|
||||
|
||||
// V2 SHA256 Test
|
||||
expected.type = Fingerprint::Type::SHA256;
|
||||
expected.data = QByteArray::fromRawData(
|
||||
"\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23"
|
||||
"\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23\x23",
|
||||
32
|
||||
);
|
||||
actual = Fingerprint::fromDbLine("v2:sha256:2323232323232323232323232323232323232323232323232323232323232323");
|
||||
QCOMPARE(actual, expected);
|
||||
|
||||
// V2 SHA256 Invalid Input
|
||||
actual = Fingerprint::fromDbLine("v2:sha256:232323232323232323232323232323232323232323232323232323");
|
||||
QCOMPARE_NE(actual, expected);
|
||||
}
|
||||
|
||||
void FingerprintTests::test_typeToString()
|
||||
{
|
||||
Fingerprint expected;
|
||||
|
||||
expected.type = Fingerprint::Type::Invalid;
|
||||
QCOMPARE(expected.type, Fingerprint::Type::Invalid);
|
||||
QCOMPARE(Fingerprint::typeToString(expected.type), QStringLiteral("invalid"));
|
||||
|
||||
expected.type = Fingerprint::Type::SHA1;
|
||||
QCOMPARE(expected.type, Fingerprint::Type::SHA1);
|
||||
QCOMPARE(Fingerprint::typeToString(expected.type), QStringLiteral("sha1"));
|
||||
|
||||
expected.type = Fingerprint::Type::SHA256;
|
||||
QCOMPARE(expected.type, Fingerprint::Type::SHA256);
|
||||
QCOMPARE(Fingerprint::typeToString(expected.type), QStringLiteral("sha256"));
|
||||
}
|
||||
|
||||
void FingerprintTests::test_typeFromString()
|
||||
{
|
||||
QCOMPARE(Fingerprint::Type::SHA1, Fingerprint::typeFromString("sha1"));
|
||||
QCOMPARE(Fingerprint::Type::SHA1, Fingerprint::typeFromString("SHA1"));
|
||||
QCOMPARE(Fingerprint::Type::SHA256, Fingerprint::typeFromString("sha256"));
|
||||
QCOMPARE(Fingerprint::Type::SHA256, Fingerprint::typeFromString("SHA256"));
|
||||
|
||||
QCOMPARE(Fingerprint::Type::Invalid, Fingerprint::typeFromString("invalid"));
|
||||
QCOMPARE(Fingerprint::Type::Invalid, Fingerprint::typeFromString(""));
|
||||
QCOMPARE(Fingerprint::Type::Invalid, Fingerprint::typeFromString("230p89jivon345"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(FingerprintTests)
|
||||
18
src/unittests/net/FingerprintTests.h
Normal file
18
src/unittests/net/FingerprintTests.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include <QTest>
|
||||
|
||||
class FingerprintTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void test_isValid();
|
||||
void test_toDbLine();
|
||||
void test_fromDbLine();
|
||||
void test_typeToString();
|
||||
void test_typeFromString();
|
||||
};
|
||||
@ -13,9 +13,11 @@ using namespace deskflow;
|
||||
|
||||
void SecureUtilsTests::checkHex()
|
||||
{
|
||||
std::vector<uint8_t> fingerprint = {40, 253, 10, 152, 138, 14, 161, 108, 215, 232, 108, 167, 238, 88, 65, 113,
|
||||
202, 178, 142, 73, 37, 148, 144, 37, 38, 5, 141, 175, 99, 237, 46, 48};
|
||||
|
||||
QByteArray fingerprint(
|
||||
"\x28\xFD\x0A\x98\x8A\x0E\xA1\x6C\xD7\xE8\x6C\xA7\xEE\x58\x41\x71\xCA\xB2\x8E\x49\x25\x94\x90\x25\x26\x05\x8D\xAF"
|
||||
"\x63\xED\x2E\x30",
|
||||
32
|
||||
);
|
||||
QCOMPARE(
|
||||
deskflow::formatSSLFingerprint(fingerprint, true),
|
||||
"28:FD:0A:98:8A:0E:A1:6C:D7:E8:6C:A7:EE:58:41:71:CA:B2:8E:49:25:94:90:25:26:05:8D:AF:63:ED:2E:30"
|
||||
@ -24,8 +26,11 @@ void SecureUtilsTests::checkHex()
|
||||
|
||||
void SecureUtilsTests::checkArt()
|
||||
{
|
||||
std::vector<uint8_t> fingerprint = {40, 253, 10, 152, 138, 14, 161, 108, 215, 232, 108, 167, 238, 88, 65, 113,
|
||||
202, 178, 142, 73, 37, 148, 144, 37, 38, 5, 141, 175, 99, 237, 46, 48};
|
||||
QByteArray fingerprint(
|
||||
"\x28\xFD\x0A\x98\x8A\x0E\xA1\x6C\xD7\xE8\x6C\xA7\xEE\x58\x41\x71\xCA\xB2\x8E\x49\x25\x94\x90\x25\x26\x05\x8D\xAF"
|
||||
"\x63\xED\x2E\x30",
|
||||
32
|
||||
);
|
||||
QCOMPARE(
|
||||
deskflow::generateFingerprintArt(fingerprint), "╔═════════════════╗\n"
|
||||
"║*X+. . ║\n"
|
||||
|
||||
Reference in New Issue
Block a user