feat: use sha256
based-on:a428b61c7dbased-on:b7757fbd68based-on:a238b27879
This commit is contained in:
parent
743c3feef3
commit
fbaa0a8433
8 changed files with 280 additions and 39 deletions
|
|
@ -36,6 +36,7 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QFont>
|
||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
|
@ -471,27 +472,44 @@ void MainWindow::showMyFingerprint()
|
||||||
{
|
{
|
||||||
auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintLocalFilename).toStdString();
|
auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintLocalFilename).toStdString();
|
||||||
if (!QFile::exists(QString::fromStdString(localPath))) {
|
if (!QFile::exists(QString::fromStdString(localPath))) {
|
||||||
QMessageBox::information(
|
if (regenerateLocalFingerprints())
|
||||||
this, tr("TLS fingerprint Error"),
|
showMyFingerprint();
|
||||||
tr("Unable to read localfinger print: %1\n You may want to regenerate your keys.")
|
|
||||||
.arg(QString::fromStdString(localPath))
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
deskflow::FingerprintDatabase db;
|
deskflow::FingerprintDatabase db;
|
||||||
db.read(localPath);
|
db.read(localPath);
|
||||||
if (db.fingerprints().empty()) {
|
if (db.fingerprints().size() != 2) {
|
||||||
QMessageBox::information(
|
if (regenerateLocalFingerprints())
|
||||||
this, tr("TLS fingerprint Error"),
|
showMyFingerprint();
|
||||||
tr("Unable to read localDatabase\n You may want to regenerate your keys.")
|
|
||||||
.arg(QString::fromStdString(localPath))
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fingerprint = QString::fromStdString(deskflow::formatSSLFingerprint(db.fingerprints().front().data));
|
QString message = QStringLiteral("\n");
|
||||||
QMessageBox::information(this, "TLS fingerprint", fingerprint);
|
for (const auto &fingerprint : db.fingerprints()) {
|
||||||
|
if (fingerprint.algorithm == "sha1") {
|
||||||
|
message.append(
|
||||||
|
QStringLiteral("\nSHA1\n%1\n").arg(QString::fromStdString(deskflow::formatSSLFingerprint(fingerprint.data)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fingerprint.algorithm == "sha256") {
|
||||||
|
message.append(QStringLiteral("\nSHA256\n%1\n%2\n")
|
||||||
|
.arg(
|
||||||
|
QString::fromStdString(deskflow::formatSSLFingerprintColumns(fingerprint.data)),
|
||||||
|
QString::fromStdString(deskflow::generateFingerprintArt(fingerprint.data))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO This dialog better in the future
|
||||||
|
QMessageBox mbox(this);
|
||||||
|
mbox.setWindowTitle(tr("Your Fingerprints"));
|
||||||
|
auto font = new QFont("Monospace");
|
||||||
|
font->setStyleHint(QFont::TypeWriter);
|
||||||
|
mbox.setFont(*font);
|
||||||
|
mbox.setText(message);
|
||||||
|
mbox.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setModeServer()
|
void MainWindow::setModeServer()
|
||||||
|
|
@ -688,19 +706,28 @@ void MainWindow::checkConnected(const QString &line)
|
||||||
|
|
||||||
void MainWindow::checkFingerprint(const QString &line)
|
void MainWindow::checkFingerprint(const QString &line)
|
||||||
{
|
{
|
||||||
static const QRegularExpression re(".*server fingerprint: ([A-F0-9:]+)");
|
static const QRegularExpression re(R"(.*server fingerprint: \(SHA1\) ([A-F0-9:]+) \(SHA256\) ([A-F0-9:]+))");
|
||||||
auto match = re.match(line);
|
auto match = re.match(line);
|
||||||
if (!match.hasMatch()) {
|
if (!match.hasMatch()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintTrustedServersFilename).toStdString();
|
const deskflow::FingerprintData sha1 = {
|
||||||
|
deskflow::fingerprintTypeToString(deskflow::FingerprintType::SHA1),
|
||||||
|
deskflow::string::fromHex(match.captured(1).toStdString())
|
||||||
|
};
|
||||||
|
|
||||||
|
const deskflow::FingerprintData sha256 = {
|
||||||
|
deskflow::fingerprintTypeToString(deskflow::FingerprintType::SHA256),
|
||||||
|
deskflow::string::fromHex(match.captured(2).toStdString())
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Only Save the sha256
|
||||||
|
auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintTrustedServersFilename).toStdString();
|
||||||
deskflow::FingerprintDatabase db;
|
deskflow::FingerprintDatabase db;
|
||||||
db.read(localPath);
|
db.read(localPath);
|
||||||
|
if (db.isTrusted(sha256)) {
|
||||||
const deskflow::FingerprintData fingerprint{"sha1", deskflow::string::fromHex(match.captured(1).toStdString())};
|
|
||||||
if (db.isTrusted(fingerprint)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -713,19 +740,25 @@ void MainWindow::checkFingerprint(const QString &line)
|
||||||
QMessageBox::StandardButton fingerprintReply = QMessageBox::information(
|
QMessageBox::StandardButton fingerprintReply = QMessageBox::information(
|
||||||
this, tr("Security question"),
|
this, tr("Security question"),
|
||||||
tr("<p>You are connecting to a server.</p>"
|
tr("<p>You are connecting to a server.</p>"
|
||||||
"<p>Here is it's TLS fingerprint:</p>"
|
"<p>Here is it's TLS fingerprint:</p>\n\n"
|
||||||
"<p>%1</p>"
|
"<p>SHA256:%1\n"
|
||||||
|
"<p>%2\n\n"
|
||||||
|
"<p>SHA1(Obsolete): Compare for old versions only: %3</p>"
|
||||||
"<p>Compare this fingerprint to the one on your server's screen. "
|
"<p>Compare this fingerprint to the one on your server's screen. "
|
||||||
"If the two don't match exactly, then it's probably not the server "
|
"If the two don't match exactly, then it's probably not the server "
|
||||||
"you're expecting (it could be a malicious user).</p>"
|
"you're expecting (it could be a malicious user).</p>"
|
||||||
"<p>Do you want to trust this fingerprint for future "
|
"<p>Do you want to trust this fingerprint for future "
|
||||||
"connections? If you don't, a connection cannot be made.</p>")
|
"connections? If you don't, a connection cannot be made.</p>")
|
||||||
.arg(QString::fromStdString(deskflow::formatSSLFingerprint(fingerprint.data))),
|
.arg(
|
||||||
|
QString::fromStdString(deskflow::formatSSLFingerprint(sha256.data)),
|
||||||
|
QString::fromStdString(deskflow::generateFingerprintArt(sha256.data)),
|
||||||
|
QString::fromStdString(deskflow::formatSSLFingerprint(sha1.data))
|
||||||
|
),
|
||||||
QMessageBox::Yes | QMessageBox::No
|
QMessageBox::Yes | QMessageBox::No
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fingerprintReply == QMessageBox::Yes) {
|
if (fingerprintReply == QMessageBox::Yes) {
|
||||||
db.addTrusted(fingerprint);
|
db.addTrusted(sha256);
|
||||||
db.write(localPath);
|
db.write(localPath);
|
||||||
m_coreProcess.start();
|
m_coreProcess.start();
|
||||||
}
|
}
|
||||||
|
|
@ -1063,3 +1096,13 @@ QString MainWindow::getTlsPath()
|
||||||
CoreTool coreTool;
|
CoreTool coreTool;
|
||||||
return QStringLiteral("%1/%2").arg(coreTool.getProfileDir(), kSslDir);
|
return QStringLiteral("%1/%2").arg(coreTool.getProfileDir(), kSslDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainWindow::regenerateLocalFingerprints()
|
||||||
|
{
|
||||||
|
TlsCertificate tls;
|
||||||
|
if (!tls.generateFingerprint(m_appConfig.tlsCertPath())) {
|
||||||
|
QMessageBox::critical(this, tr("TLS Fingerprint Error"), tr("Failed to calculate keys"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,10 @@ private:
|
||||||
|
|
||||||
QString getTlsPath();
|
QString getTlsPath();
|
||||||
|
|
||||||
|
// Generate prints if they are missing
|
||||||
|
// Returns true if successful
|
||||||
|
bool regenerateLocalFingerprints();
|
||||||
|
|
||||||
VersionChecker m_versionChecker;
|
VersionChecker m_versionChecker;
|
||||||
bool m_secureSocket = false;
|
bool m_secureSocket = false;
|
||||||
deskflow::gui::config::ServerConfigDialogState m_serverConfigDialogState;
|
deskflow::gui::config::ServerConfigDialogState m_serverConfigDialogState;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "TlsCertificate.h"
|
#include "TlsCertificate.h"
|
||||||
|
|
||||||
|
#include "base/finally.h"
|
||||||
#include "common/constants.h"
|
#include "common/constants.h"
|
||||||
#include "gui/core/CoreTool.h"
|
#include "gui/core/CoreTool.h"
|
||||||
#include "net/FingerprintData.h"
|
#include "net/FingerprintData.h"
|
||||||
|
|
@ -17,8 +18,17 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent)
|
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
CoreTool coreTool;
|
||||||
|
m_profileDir = coreTool.getProfileDir();
|
||||||
|
if (m_profileDir.isEmpty())
|
||||||
|
qCritical() << "unable to get profile dir";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TlsCertificate::generateCertificate(const QString &path, int keyLength)
|
bool TlsCertificate::generateCertificate(const QString &path, int keyLength)
|
||||||
|
|
@ -45,18 +55,12 @@ bool TlsCertificate::generateCertificate(const QString &path, int keyLength)
|
||||||
bool TlsCertificate::generateFingerprint(const QString &certificateFilename)
|
bool TlsCertificate::generateFingerprint(const QString &certificateFilename)
|
||||||
{
|
{
|
||||||
qDebug("generating tls fingerprint");
|
qDebug("generating tls fingerprint");
|
||||||
|
const std::string certPath = certificateFilename.toStdString();
|
||||||
try {
|
try {
|
||||||
auto fingerprint =
|
|
||||||
deskflow::pemFileCertFingerprint(certificateFilename.toStdString(), deskflow::FingerprintType::SHA1);
|
|
||||||
|
|
||||||
CoreTool coreTool;
|
|
||||||
QString profileDir = coreTool.getProfileDir();
|
|
||||||
|
|
||||||
auto localPath = QStringLiteral("%1/%2/%3").arg(profileDir, kSslDir, kFingerprintLocalFilename).toStdString();
|
|
||||||
|
|
||||||
deskflow::FingerprintDatabase db;
|
deskflow::FingerprintDatabase db;
|
||||||
db.addTrusted(fingerprint);
|
db.addTrusted(deskflow::pemFileCertFingerprint(certPath, deskflow::FingerprintType::SHA1));
|
||||||
db.write(localPath);
|
db.addTrusted(deskflow::pemFileCertFingerprint(certPath, deskflow::FingerprintType::SHA256));
|
||||||
|
db.write(QStringLiteral("%1/%2").arg(getTlsDir(), kFingerprintLocalFilename).toStdString());
|
||||||
|
|
||||||
qDebug("tls fingerprint generated");
|
qDebug("tls fingerprint generated");
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -70,3 +74,55 @@ int TlsCertificate::getCertKeyLength(const QString &path)
|
||||||
{
|
{
|
||||||
return deskflow::getCertLength(path.toStdString());
|
return deskflow::getCertLength(path.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString TlsCertificate::getCertificatePath() const
|
||||||
|
{
|
||||||
|
return QStringLiteral("%1/%2/%3").arg(m_profileDir, kSslDir, kCertificateFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TlsCertificate::getTlsDir() const
|
||||||
|
{
|
||||||
|
return QStringLiteral("%1/%2").arg(m_profileDir, kSslDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TlsCertificate::isCertificateValid(const QString &path)
|
||||||
|
{
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
ERR_load_crypto_strings();
|
||||||
|
|
||||||
|
auto fp = deskflow::fopenUtf8Path(path.toStdString(), "r");
|
||||||
|
if (!fp) {
|
||||||
|
qWarning() << tr("could not read from default certificate file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto fileClose = deskflow::finally([fp]() { std::fclose(fp); });
|
||||||
|
|
||||||
|
auto *cert = PEM_read_X509(fp, nullptr, nullptr, nullptr);
|
||||||
|
if (!cert) {
|
||||||
|
qWarning() << tr("could not load default certificate file to memory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto certFree = deskflow::finally([cert]() { X509_free(cert); });
|
||||||
|
|
||||||
|
auto *pubkey = X509_get_pubkey(cert);
|
||||||
|
if (!pubkey) {
|
||||||
|
qWarning() << tr("default certificate key file does not contain valid public key");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto pubkeyFree = deskflow::finally([pubkey]() { EVP_PKEY_free(pubkey); });
|
||||||
|
|
||||||
|
auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey));
|
||||||
|
if (type != EVP_PKEY_RSA && type != EVP_PKEY_DSA) {
|
||||||
|
qWarning() << tr("public key in default certificate key file is not RSA or DSA");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bits = EVP_PKEY_bits(pubkey);
|
||||||
|
if (bits < 2048) {
|
||||||
|
// We could have small keys in old barrier installations
|
||||||
|
qWarning() << tr("public key in default certificate key file is too small");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,13 @@ class TlsCertificate : public QObject
|
||||||
public:
|
public:
|
||||||
explicit TlsCertificate(QObject *parent = nullptr);
|
explicit TlsCertificate(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
bool isCertificateValid(const QString &path);
|
||||||
bool generateCertificate(const QString &path, int keyLength);
|
bool generateCertificate(const QString &path, int keyLength);
|
||||||
|
bool generateFingerprint(const QString &certificateFilename);
|
||||||
int getCertKeyLength(const QString &path);
|
int getCertKeyLength(const QString &path);
|
||||||
|
QString getCertificatePath() const;
|
||||||
|
QString getTlsDir() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool generateFingerprint(const QString &certificateFilename);
|
QString m_profileDir;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -612,16 +612,22 @@ void SecureSocket::disconnect()
|
||||||
|
|
||||||
bool SecureSocket::verifyCertFingerprint()
|
bool SecureSocket::verifyCertFingerprint()
|
||||||
{
|
{
|
||||||
// calculate received certificate fingerprint
|
deskflow::FingerprintData sha1;
|
||||||
deskflow::FingerprintData fingerprint;
|
deskflow::FingerprintData sha256;
|
||||||
try {
|
try {
|
||||||
fingerprint = deskflow::sslCertFingerprint(SSL_get_peer_certificate(m_ssl->m_ssl), deskflow::FingerprintType::SHA1);
|
auto cert = SSL_get_peer_certificate(m_ssl->m_ssl);
|
||||||
|
sha1 = deskflow::sslCertFingerprint(cert, deskflow::FingerprintType::SHA1);
|
||||||
|
sha256 = deskflow::sslCertFingerprint(cert, deskflow::FingerprintType::SHA256);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
LOG((CLOG_ERR "%s", e.what()));
|
LOG((CLOG_ERR "%s", e.what()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG((CLOG_NOTE "server fingerprint: %s", deskflow::formatSSLFingerprint(fingerprint.data).c_str()));
|
// Gui Must Parse these two lines, DO NOT CHANGE
|
||||||
|
LOG(
|
||||||
|
(CLOG_NOTE "server fingerprint: (SHA1) %s (SHA256) %s", deskflow::formatSSLFingerprint(sha1.data).c_str(),
|
||||||
|
deskflow::formatSSLFingerprint(sha256.data).c_str())
|
||||||
|
);
|
||||||
|
|
||||||
std::string trustedServersFilename = deskflow::string::sprintf(
|
std::string trustedServersFilename = deskflow::string::sprintf(
|
||||||
"%s/%s/%s", ARCH->getProfileDirectory().c_str(), kSslDir, kFingerprintTrustedServersFilename
|
"%s/%s/%s", ARCH->getProfileDirectory().c_str(), kSslDir, kFingerprintTrustedServersFilename
|
||||||
|
|
@ -641,7 +647,7 @@ bool SecureSocket::verifyCertFingerprint()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!db.isTrusted(fingerprint)) {
|
if (!db.isTrusted(sha256)) {
|
||||||
LOG((CLOG_WARN "fingerprint does not match trusted fingerprint"));
|
LOG((CLOG_WARN "fingerprint does not match trusted fingerprint"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SecureUtils.h"
|
#include "SecureUtils.h"
|
||||||
#include "FingerprintDatabase.h"
|
|
||||||
#include "base/String.h"
|
#include "base/String.h"
|
||||||
#include "base/finally.h"
|
#include "base/finally.h"
|
||||||
#include "io/filesystem.h"
|
#include "io/filesystem.h"
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/x509v3.h>
|
#include <openssl/x509v3.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace deskflow {
|
namespace deskflow {
|
||||||
|
|
@ -154,4 +156,107 @@ int getCertLength(const std::string &path)
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string formatSSLFingerprintColumns(const std::vector<uint8_t> &fingerprint)
|
||||||
|
{
|
||||||
|
auto kmaxColumns = 8;
|
||||||
|
|
||||||
|
std::string hex = deskflow::string::toHex(fingerprint, 2);
|
||||||
|
deskflow::string::uppercase(hex);
|
||||||
|
if (hex.empty() || hex.size() % 2 != 0) {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
separated.pop_back(); // we don't need last newline character
|
||||||
|
return separated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Draw an ASCII-Art representing the fingerprint so human brain can
|
||||||
|
profit from its built-in pattern recognition ability.
|
||||||
|
This technique is called "random art" and can be found in some
|
||||||
|
scientific publications like this original paper:
|
||||||
|
"Hash Visualization: a New Technique to improve Real-World Security",
|
||||||
|
Perrig A. and Song D., 1999, International Workshop on Cryptographic
|
||||||
|
Techniques and E-Commerce (CrypTEC '99)
|
||||||
|
sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
|
||||||
|
The subject came up in a talk by Dan Kaminsky, too.
|
||||||
|
If you see the picture is different, the key is different.
|
||||||
|
If the picture looks the same, you still know nothing.
|
||||||
|
The algorithm used here is a worm crawling over a discrete plane,
|
||||||
|
leaving a trace (augmenting the field) everywhere it goes.
|
||||||
|
Movement is taken from rawDigest 2bit-wise. Bumping into walls
|
||||||
|
makes the respective movement vector be ignored for this turn.
|
||||||
|
Graphs are not unambiguous, because circles in graphs can be
|
||||||
|
walked in either direction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Field sizes for the random art. Have to be odd, so the starting point
|
||||||
|
can be in the exact middle of the picture, and `baseSize` should be >=8 .
|
||||||
|
Else pictures would be too dense, and drawing the frame would
|
||||||
|
fail, too, because the key type would not fit in anymore.
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::string generateFingerprintArt(const std::vector<std::uint8_t> &rawDigest)
|
||||||
|
{
|
||||||
|
const auto baseSize = 8;
|
||||||
|
const auto rows = (baseSize + 1);
|
||||||
|
const auto columns = (baseSize * 2 + 1);
|
||||||
|
const std::string characterPool = " .o+=*BOX@%&#/^SE";
|
||||||
|
const std::size_t len = characterPool.length() - 1;
|
||||||
|
|
||||||
|
std::uint8_t field[columns][rows];
|
||||||
|
memset(field, 0, columns * rows * sizeof(char));
|
||||||
|
int x = columns / 2;
|
||||||
|
int y = rows / 2;
|
||||||
|
|
||||||
|
/* process raw key */
|
||||||
|
for (size_t i = 0; i < rawDigest.size(); i++) {
|
||||||
|
/* each byte conveys four 2-bit move commands */
|
||||||
|
int input = rawDigest[i];
|
||||||
|
for (uint32_t b = 0; b < 4; b++) {
|
||||||
|
/* evaluate 2 bit, rest is shifted later */
|
||||||
|
x += (input & 0x1) ? 1 : -1;
|
||||||
|
y += (input & 0x2) ? 1 : -1;
|
||||||
|
|
||||||
|
/* assure we are still in bounds */
|
||||||
|
x = std::clamp(x, 0, columns - 1);
|
||||||
|
y = std::clamp(y, 0, rows - 1);
|
||||||
|
|
||||||
|
/* augment the field */
|
||||||
|
if (field[x][y] < len - 2)
|
||||||
|
field[x][y]++;
|
||||||
|
input = input >> 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark starting point and end point*/
|
||||||
|
field[columns / 2][rows / 2] = len - 1;
|
||||||
|
field[x][y] = len;
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
result.reserve((columns + 3) * (rows + 2));
|
||||||
|
result.append("╔═════════════════╗\n");
|
||||||
|
|
||||||
|
/* output content */
|
||||||
|
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("║\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append("╚═════════════════╝");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace deskflow
|
} // namespace deskflow
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ namespace deskflow {
|
||||||
*/
|
*/
|
||||||
std::string formatSSLFingerprint(const std::vector<uint8_t> &fingerprint, bool enableSeparators = true);
|
std::string formatSSLFingerprint(const std::vector<uint8_t> &fingerprint, bool enableSeparators = true);
|
||||||
|
|
||||||
|
std::string formatSSLFingerprintColumns(const std::vector<uint8_t> &fingerprint);
|
||||||
|
|
||||||
FingerprintData sslCertFingerprint(X509 *cert, FingerprintType type);
|
FingerprintData sslCertFingerprint(X509 *cert, FingerprintType type);
|
||||||
|
|
||||||
FingerprintData pemFileCertFingerprint(const std::string &path, FingerprintType type);
|
FingerprintData pemFileCertFingerprint(const std::string &path, FingerprintType type);
|
||||||
|
|
@ -31,4 +33,6 @@ FingerprintData pemFileCertFingerprint(const std::string &path, FingerprintType
|
||||||
void generatePemSelfSignedCert(const std::string &path, int keyLength = 2048);
|
void generatePemSelfSignedCert(const std::string &path, int keyLength = 2048);
|
||||||
|
|
||||||
int getCertLength(const std::string &path);
|
int getCertLength(const std::string &path);
|
||||||
|
|
||||||
|
std::string generateFingerprintArt(const std::vector<std::uint8_t> &rawDigest);
|
||||||
} // namespace deskflow
|
} // namespace deskflow
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,22 @@ TEST(SecureUtilsTest, formatSSLFingerprints_fromHex_withSeperators)
|
||||||
"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"
|
"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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SecureUtilsTest, createFingerprintArt)
|
||||||
|
{
|
||||||
|
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};
|
||||||
|
ASSERT_EQ(
|
||||||
|
deskflow::generateFingerprintArt(fingerprint), "╔═════════════════╗\n"
|
||||||
|
"║*X+. . ║\n"
|
||||||
|
"║*oo + ║\n"
|
||||||
|
"║ + = ║\n"
|
||||||
|
"║ B . . ║\n"
|
||||||
|
"║.+... o S ║\n"
|
||||||
|
"║E+ ++. . ║\n"
|
||||||
|
"║B*++.. . ║\n"
|
||||||
|
"║+o*o o . ║\n"
|
||||||
|
"║+o*Bo . ║\n"
|
||||||
|
"╚═════════════════╝"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue