Merge pull request #1343 from p12tic/sha256-fingerprints
Add support for SHA256 fingerprints
This commit is contained in:
commit
22ac14be8c
|
@ -91,7 +91,6 @@ if (UNIX)
|
||||||
check_function_exists (poll HAVE_POLL)
|
check_function_exists (poll HAVE_POLL)
|
||||||
check_function_exists (sigwait HAVE_POSIX_SIGWAIT)
|
check_function_exists (sigwait HAVE_POSIX_SIGWAIT)
|
||||||
check_function_exists (strftime HAVE_STRFTIME)
|
check_function_exists (strftime HAVE_STRFTIME)
|
||||||
check_function_exists (vsnprintf HAVE_VSNPRINTF)
|
|
||||||
check_function_exists (inet_aton HAVE_INET_ATON)
|
check_function_exists (inet_aton HAVE_INET_ATON)
|
||||||
|
|
||||||
# For some reason, the check_function_exists macro doesn't detect
|
# For some reason, the check_function_exists macro doesn't detect
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Barrier no longer uses openssl CLI tool for any operations and hooks into the openssl library directly.
|
|
@ -0,0 +1,3 @@
|
||||||
|
Added support for randomart images for easier comparison of SSL
|
||||||
|
certificate fingerprints. The algorithm is identical to what
|
||||||
|
OpenSSH uses.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Barrier now uses SHA256 fingerprints for establishing security of encrypted SSL connections.
|
||||||
|
After upgrading client to new version the existing server fingerprint will need to be approved again.
|
||||||
|
Client and server will show both SHA1 and SHA256 server fingerprints to allow interoperability
|
||||||
|
with older versions of Barrier.
|
|
@ -94,9 +94,6 @@
|
||||||
/* Define to 1 if you have the <unistd.h> header file. */
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H}
|
#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H}
|
||||||
|
|
||||||
/* Define to 1 if you have the `vsnprintf` function. */
|
|
||||||
#cmakedefine HAVE_VSNPRINTF ${HAVE_VSNPRINTF}
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <wchar.h> header file. */
|
/* Define to 1 if you have the <wchar.h> header file. */
|
||||||
#cmakedefine HAVE_WCHAR_H ${HAVE_WCHAR_H}
|
#cmakedefine HAVE_WCHAR_H ${HAVE_WCHAR_H}
|
||||||
|
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
#
|
|
||||||
# Barrier OpenSSL configuration file.
|
|
||||||
# Used for generation of certificate requests.
|
|
||||||
#
|
|
||||||
|
|
||||||
dir = .
|
|
||||||
|
|
||||||
[ca]
|
|
||||||
default_ca = CA_default
|
|
||||||
|
|
||||||
[CA_default]
|
|
||||||
serial = $dir/serial
|
|
||||||
database = $dir/certindex.txt
|
|
||||||
new_certs_dir = $dir/certs
|
|
||||||
certificate = $dir/cacert.pem
|
|
||||||
private_key = $dir/private/cakey.pem
|
|
||||||
default_days = 365
|
|
||||||
default_md = md5
|
|
||||||
preserve = no
|
|
||||||
email_in_dn = no
|
|
||||||
nameopt = default_ca
|
|
||||||
certopt = default_ca
|
|
||||||
policy = policy_match
|
|
||||||
|
|
||||||
[policy_match]
|
|
||||||
countryName = match
|
|
||||||
stateOrProvinceName = match
|
|
||||||
organizationName = match
|
|
||||||
organizationalUnitName = optional
|
|
||||||
commonName = supplied
|
|
||||||
emailAddress = optional
|
|
||||||
|
|
||||||
[req]
|
|
||||||
default_bits = 2048 # Size of keys
|
|
||||||
default_keyfile = key.pem # name of generated keys
|
|
||||||
default_md = md5 # message digest algorithm
|
|
||||||
string_mask = nombstr # permitted characters
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
req_extensions = v3_req
|
|
||||||
|
|
||||||
[req_distinguished_name]
|
|
||||||
0.organizationName = Organization Name (company)
|
|
||||||
organizationalUnitName = Organizational Unit Name (department, division)
|
|
||||||
emailAddress = Email Address
|
|
||||||
emailAddress_max = 40
|
|
||||||
localityName = Locality Name (city, district)
|
|
||||||
stateOrProvinceName = State or Province Name (full name)
|
|
||||||
countryName = Country Name (2 letter code)
|
|
||||||
countryName_min = 2
|
|
||||||
countryName_max = 2
|
|
||||||
commonName = Common Name (hostname, IP, or your name)
|
|
||||||
commonName_max = 64
|
|
||||||
0.organizationName_default = My Company
|
|
||||||
localityName_default = My Town
|
|
||||||
stateOrProvinceName_default = State or Providence
|
|
||||||
countryName_default = US
|
|
||||||
|
|
||||||
[v3_ca]
|
|
||||||
basicConstraints = CA:TRUE
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
authorityKeyIdentifier = keyid:always,issuer:always
|
|
||||||
|
|
||||||
[v3_req]
|
|
||||||
basicConstraints = CA:FALSE
|
|
||||||
subjectKeyIdentifier = hash
|
|
|
@ -29,7 +29,6 @@ set(GUI_SOURCE_FILES
|
||||||
src/CommandProcess.cpp
|
src/CommandProcess.cpp
|
||||||
src/DataDownloader.cpp
|
src/DataDownloader.cpp
|
||||||
src/DisplayIsValid.cpp
|
src/DisplayIsValid.cpp
|
||||||
src/Fingerprint.cpp
|
|
||||||
src/HotkeyDialog.cpp
|
src/HotkeyDialog.cpp
|
||||||
src/IpcClient.cpp
|
src/IpcClient.cpp
|
||||||
src/Ipc.cpp
|
src/Ipc.cpp
|
||||||
|
@ -70,7 +69,6 @@ set(GUI_HEADER_FILES
|
||||||
src/DataDownloader.h
|
src/DataDownloader.h
|
||||||
src/DisplayIsValid.h
|
src/DisplayIsValid.h
|
||||||
src/ElevateMode.h
|
src/ElevateMode.h
|
||||||
src/Fingerprint.h
|
|
||||||
src/HotkeyDialog.h
|
src/HotkeyDialog.h
|
||||||
src/IpcClient.h
|
src/IpcClient.h
|
||||||
src/Ipc.h
|
src/Ipc.h
|
||||||
|
@ -131,7 +129,7 @@ add_executable (barrier WIN32
|
||||||
|
|
||||||
include_directories (./src)
|
include_directories (./src)
|
||||||
|
|
||||||
target_link_libraries (barrier Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS})
|
target_link_libraries(barrier net base io Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS})
|
||||||
target_compile_definitions (barrier PRIVATE -DBARRIER_VERSION_STAGE="${BARRIER_VERSION_STAGE}")
|
target_compile_definitions (barrier PRIVATE -DBARRIER_VERSION_STAGE="${BARRIER_VERSION_STAGE}")
|
||||||
target_compile_definitions (barrier PRIVATE -DBARRIER_REVISION="${BARRIER_REVISION}")
|
target_compile_definitions (barrier PRIVATE -DBARRIER_REVISION="${BARRIER_REVISION}")
|
||||||
|
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
/*
|
|
||||||
* barrier -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2015-2016 Symless Ltd.
|
|
||||||
*
|
|
||||||
* This package is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* found in the file LICENSE that should have accompanied this file.
|
|
||||||
*
|
|
||||||
* This package 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 "Fingerprint.h"
|
|
||||||
|
|
||||||
#include "common/DataDirectories.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QTextStream>
|
|
||||||
|
|
||||||
static const char kDirName[] = "SSL/Fingerprints";
|
|
||||||
static const char kLocalFilename[] = "Local.txt";
|
|
||||||
static const char kTrustedServersFilename[] = "TrustedServers.txt";
|
|
||||||
static const char kTrustedClientsFilename[] = "TrustedClients.txt";
|
|
||||||
|
|
||||||
Fingerprint::Fingerprint(const QString& filename)
|
|
||||||
{
|
|
||||||
m_Filename = filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fingerprint::trust(const QString& fingerprintText, bool append)
|
|
||||||
{
|
|
||||||
Fingerprint::persistDirectory();
|
|
||||||
|
|
||||||
QIODevice::OpenMode openMode;
|
|
||||||
if (append) {
|
|
||||||
openMode = QIODevice::Append;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
openMode = QIODevice::WriteOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile file(filePath());
|
|
||||||
if (file.open(openMode))
|
|
||||||
{
|
|
||||||
QTextStream out(&file);
|
|
||||||
out << fingerprintText << "\n";
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Fingerprint::fileExists() const
|
|
||||||
{
|
|
||||||
QString dirName = Fingerprint::directoryPath();
|
|
||||||
if (!QDir(dirName).exists()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile file(filePath());
|
|
||||||
return file.exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Fingerprint::isTrusted(const QString& fingerprintText)
|
|
||||||
{
|
|
||||||
QStringList list = readList();
|
|
||||||
for (QString trusted : list) {
|
|
||||||
if (trusted == fingerprintText) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList Fingerprint::readList(const int readTo)
|
|
||||||
{
|
|
||||||
QStringList list;
|
|
||||||
|
|
||||||
QString dirName = Fingerprint::directoryPath();
|
|
||||||
if (!QDir(dirName).exists()) {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile file(filePath());
|
|
||||||
|
|
||||||
if (file.open(QIODevice::ReadOnly))
|
|
||||||
{
|
|
||||||
QTextStream in(&file);
|
|
||||||
while (!in.atEnd())
|
|
||||||
{
|
|
||||||
list.append(in.readLine());
|
|
||||||
if (list.size() == readTo) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Fingerprint::readFirst()
|
|
||||||
{
|
|
||||||
QStringList list = readList(1);
|
|
||||||
return list.at(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Fingerprint::filePath() const
|
|
||||||
{
|
|
||||||
QString dir = Fingerprint::directoryPath();
|
|
||||||
return QString("%1/%2").arg(dir).arg(m_Filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fingerprint::persistDirectory()
|
|
||||||
{
|
|
||||||
QDir dir(Fingerprint::directoryPath());
|
|
||||||
if (!dir.exists()) {
|
|
||||||
dir.mkpath(".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Fingerprint::directoryPath()
|
|
||||||
{
|
|
||||||
auto profileDir = QString::fromStdString(DataDirectories::profile());
|
|
||||||
|
|
||||||
return QString("%1/%2")
|
|
||||||
.arg(profileDir)
|
|
||||||
.arg(kDirName);
|
|
||||||
}
|
|
||||||
|
|
||||||
Fingerprint Fingerprint::local()
|
|
||||||
{
|
|
||||||
return Fingerprint(kLocalFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
Fingerprint Fingerprint::trustedServers()
|
|
||||||
{
|
|
||||||
return Fingerprint(kTrustedServersFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
Fingerprint Fingerprint::trustedClients()
|
|
||||||
{
|
|
||||||
return Fingerprint(kTrustedClientsFilename);
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* barrier -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2015-2016 Symless Ltd.
|
|
||||||
*
|
|
||||||
* This package is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* found in the file LICENSE that should have accompanied this file.
|
|
||||||
*
|
|
||||||
* This package 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class Fingerprint
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void trust(const QString& fingerprintText, bool append = true);
|
|
||||||
bool isTrusted(const QString& fingerprintText);
|
|
||||||
QStringList readList(const int readTo = -1);
|
|
||||||
QString readFirst();
|
|
||||||
QString filePath() const;
|
|
||||||
bool fileExists() const;
|
|
||||||
|
|
||||||
static Fingerprint local();
|
|
||||||
static Fingerprint trustedServers();
|
|
||||||
static Fingerprint trustedClients();
|
|
||||||
static QString directoryPath();
|
|
||||||
static void persistDirectory();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Fingerprint(const QString& filename);
|
|
||||||
|
|
||||||
QString m_Filename;
|
|
||||||
};
|
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
|
|
||||||
#include "Fingerprint.h"
|
|
||||||
#include "AboutDialog.h"
|
#include "AboutDialog.h"
|
||||||
#include "ServerConfigDialog.h"
|
#include "ServerConfigDialog.h"
|
||||||
#include "SettingsDialog.h"
|
#include "SettingsDialog.h"
|
||||||
|
@ -31,7 +30,10 @@
|
||||||
#include "ProcessorArch.h"
|
#include "ProcessorArch.h"
|
||||||
#include "SslCertificate.h"
|
#include "SslCertificate.h"
|
||||||
#include "ShutdownCh.h"
|
#include "ShutdownCh.h"
|
||||||
|
#include "base/String.h"
|
||||||
#include "common/DataDirectories.h"
|
#include "common/DataDirectories.h"
|
||||||
|
#include "net/FingerprintDatabase.h"
|
||||||
|
#include "net/SecureUtils.h"
|
||||||
|
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
@ -156,9 +158,22 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
|
||||||
|
|
||||||
m_pComboServerList->hide();
|
m_pComboServerList->hide();
|
||||||
m_pLabelPadlock->hide();
|
m_pLabelPadlock->hide();
|
||||||
|
frame_fingerprint_details->hide();
|
||||||
|
|
||||||
updateSSLFingerprint();
|
updateSSLFingerprint();
|
||||||
|
|
||||||
|
connect(toolbutton_show_fingerprint, &QToolButton::clicked, [this](bool checked)
|
||||||
|
{
|
||||||
|
m_fingerprint_expanded = !m_fingerprint_expanded;
|
||||||
|
if (m_fingerprint_expanded) {
|
||||||
|
frame_fingerprint_details->show();
|
||||||
|
toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::UpArrow);
|
||||||
|
} else {
|
||||||
|
frame_fingerprint_details->hide();
|
||||||
|
toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::DownArrow);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// resize window to smallest reasonable size
|
// resize window to smallest reasonable size
|
||||||
resize(0, 0);
|
resize(0, 0);
|
||||||
}
|
}
|
||||||
|
@ -412,13 +427,29 @@ void MainWindow::checkConnected(const QString& line)
|
||||||
|
|
||||||
void MainWindow::checkFingerprint(const QString& line)
|
void MainWindow::checkFingerprint(const QString& line)
|
||||||
{
|
{
|
||||||
QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)");
|
QRegExp fingerprintRegex(".*server fingerprint \\(SHA1\\): ([A-F0-9:]+) \\(SHA256\\): ([A-F0-9:]+)");
|
||||||
if (!fingerprintRegex.exactMatch(line)) {
|
if (!fingerprintRegex.exactMatch(line)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString fingerprint = fingerprintRegex.cap(1);
|
barrier::FingerprintData fingerprint_sha1 = {
|
||||||
if (Fingerprint::trustedServers().isTrusted(fingerprint)) {
|
barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA1),
|
||||||
|
barrier::string::from_hex(fingerprintRegex.cap(1).toStdString())
|
||||||
|
};
|
||||||
|
|
||||||
|
barrier::FingerprintData fingerprint_sha256 = {
|
||||||
|
barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA256),
|
||||||
|
barrier::string::from_hex(fingerprintRegex.cap(2).toStdString())
|
||||||
|
};
|
||||||
|
|
||||||
|
auto db_path = DataDirectories::trusted_servers_ssl_fingerprints_path();
|
||||||
|
|
||||||
|
// We compare only SHA256 fingerprints, but show both SHA1 and SHA256 so that the users can
|
||||||
|
// still verify fingerprints on old Barrier servers. This way the only time when we are exposed
|
||||||
|
// to SHA1 vulnerabilities is when the user is reconnecting again.
|
||||||
|
barrier::FingerprintDatabase db;
|
||||||
|
db.read(db_path);
|
||||||
|
if (db.is_trusted(fingerprint_sha256)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +463,11 @@ void MainWindow::checkFingerprint(const QString& line)
|
||||||
QMessageBox::information(
|
QMessageBox::information(
|
||||||
this, tr("Security question"),
|
this, tr("Security question"),
|
||||||
tr("Do you trust this fingerprint?\n\n"
|
tr("Do you trust this fingerprint?\n\n"
|
||||||
"%1\n\n"
|
"SHA256:\n"
|
||||||
|
"%1\n"
|
||||||
|
"%2\n\n"
|
||||||
|
"SHA1 (obsolete, when using old Barrier server):\n"
|
||||||
|
"%3\n\n"
|
||||||
"This is a server fingerprint. You should compare this "
|
"This is a server fingerprint. You should compare this "
|
||||||
"fingerprint to the one on your server's screen. If the "
|
"fingerprint to the one on your server's screen. If the "
|
||||||
"two don't match exactly, then it's probably not the server "
|
"two don't match exactly, then it's probably not the server "
|
||||||
|
@ -440,12 +475,16 @@ void MainWindow::checkFingerprint(const QString& line)
|
||||||
"To automatically trust this fingerprint for future "
|
"To automatically trust this fingerprint for future "
|
||||||
"connections, click Yes. To reject this fingerprint and "
|
"connections, click Yes. To reject this fingerprint and "
|
||||||
"disconnect from the server, click No.")
|
"disconnect from the server, click No.")
|
||||||
.arg(fingerprint),
|
.arg(QString::fromStdString(barrier::format_ssl_fingerprint(fingerprint_sha256.data)))
|
||||||
|
.arg(QString::fromStdString(
|
||||||
|
barrier::create_fingerprint_randomart(fingerprint_sha256.data)))
|
||||||
|
.arg(QString::fromStdString(barrier::format_ssl_fingerprint(fingerprint_sha1.data))),
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
if (fingerprintReply == QMessageBox::Yes) {
|
if (fingerprintReply == QMessageBox::Yes) {
|
||||||
// restart core process after trusting fingerprint.
|
// restart core process after trusting fingerprint.
|
||||||
Fingerprint::trustedServers().trust(fingerprint);
|
db.add_trusted(fingerprint_sha256);
|
||||||
|
db.write(db_path);
|
||||||
startBarrier();
|
startBarrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -925,6 +964,14 @@ void MainWindow::changeEvent(QEvent* event)
|
||||||
QMainWindow::changeEvent(event);
|
QMainWindow::changeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainWindow::event(QEvent* event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::LayoutRequest) {
|
||||||
|
setFixedSize(sizeHint());
|
||||||
|
}
|
||||||
|
return QMainWindow::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::updateZeroconfService()
|
void MainWindow::updateZeroconfService()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_UpdateZeroconfMutex);
|
QMutexLocker locker(&m_UpdateZeroconfMutex);
|
||||||
|
@ -965,12 +1012,47 @@ void MainWindow::updateSSLFingerprint()
|
||||||
});
|
});
|
||||||
m_pSslCertificate->generateCertificate();
|
m_pSslCertificate->generateCertificate();
|
||||||
}
|
}
|
||||||
if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) {
|
|
||||||
m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst());
|
toolbutton_show_fingerprint->setEnabled(false);
|
||||||
m_pLabelLocalFingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
|
||||||
} else {
|
|
||||||
m_pLabelLocalFingerprint->setText("Disabled");
|
m_pLabelLocalFingerprint->setText("Disabled");
|
||||||
|
|
||||||
|
if (!m_AppConfig->getCryptoEnabled()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto local_path = DataDirectories::local_ssl_fingerprints_path();
|
||||||
|
if (!QFile::exists(QString::fromStdString(local_path))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
barrier::FingerprintDatabase db;
|
||||||
|
db.read(local_path);
|
||||||
|
if (db.fingerprints().size() != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& fingerprint : db.fingerprints()) {
|
||||||
|
if (fingerprint.algorithm == "sha1") {
|
||||||
|
auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data);
|
||||||
|
label_sha1_fingerprint_full->setText(QString::fromStdString(fingerprint_str));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fingerprint.algorithm == "sha256") {
|
||||||
|
auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data);
|
||||||
|
fingerprint_str.resize(40);
|
||||||
|
fingerprint_str += " ...";
|
||||||
|
|
||||||
|
auto fingerprint_str_cols = barrier::format_ssl_fingerprint_columns(fingerprint.data);
|
||||||
|
auto fingerprint_randomart = barrier::create_fingerprint_randomart(fingerprint.data);
|
||||||
|
|
||||||
|
m_pLabelLocalFingerprint->setText(QString::fromStdString(fingerprint_str));
|
||||||
|
label_sha256_fingerprint_full->setText(QString::fromStdString(fingerprint_str_cols));
|
||||||
|
label_sha256_randomart->setText(QString::fromStdString(fingerprint_randomart));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbutton_show_fingerprint->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_m_pGroupClient_toggled(bool on)
|
void MainWindow::on_m_pGroupClient_toggled(bool on)
|
||||||
|
|
|
@ -157,6 +157,7 @@ public slots:
|
||||||
void stopService();
|
void stopService();
|
||||||
void stopDesktop();
|
void stopDesktop();
|
||||||
void changeEvent(QEvent* event);
|
void changeEvent(QEvent* event);
|
||||||
|
bool event(QEvent* event);
|
||||||
void retranslateMenuBar();
|
void retranslateMenuBar();
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
bool isServiceRunning(QString name);
|
bool isServiceRunning(QString name);
|
||||||
|
@ -202,6 +203,8 @@ public slots:
|
||||||
QStringList m_PendingClientNames;
|
QStringList m_PendingClientNames;
|
||||||
LogWindow *m_pLogWindow;
|
LogWindow *m_pLogWindow;
|
||||||
|
|
||||||
|
bool m_fingerprint_expanded = false;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_m_pCheckBoxAutoConfig_toggled(bool checked);
|
void on_m_pCheckBoxAutoConfig_toggled(bool checked);
|
||||||
void on_m_pComboServerList_currentIndexChanged(QString );
|
void on_m_pComboServerList_currentIndexChanged(QString );
|
||||||
|
|
|
@ -2,31 +2,20 @@
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>MainWindowBase</class>
|
<class>MainWindowBase</class>
|
||||||
<widget class="QMainWindow" name="MainWindowBase">
|
<widget class="QMainWindow" name="MainWindowBase">
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>600</width>
|
|
||||||
<height>550</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>600</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Barrier</string>
|
<string>Barrier</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetFixedSize</enum>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="m_pGroupServer">
|
<widget class="QGroupBox" name="m_pGroupServer">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -86,10 +75,87 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="toolbutton_show_fingerprint">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="arrowType">
|
||||||
|
<enum>Qt::DownArrow</enum>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_fingerprint_details">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetMinimumSize</enum>
|
||||||
|
</property>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="label_sha256_randomart">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Courier</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_sha1">
|
||||||
|
<property name="text">
|
||||||
|
<string>SHA1 (deprecated, compare to old clients only):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_sha1_fingerprint_full">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_sha256">
|
||||||
|
<property name="text">
|
||||||
|
<string>SHA256:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_sha256_fingerprint_full">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="m_pRadioInternalConfig">
|
<widget class="QRadioButton" name="m_pRadioInternalConfig">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -253,7 +319,7 @@
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="pixmap">
|
<property name="pixmap">
|
||||||
<pixmap resource="Barrier.qrc">:/res/icons/16x16/padlock.png</pixmap>
|
<pixmap resource="../res/Barrier.qrc">:/res/icons/16x16/padlock.png</pixmap>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -399,7 +465,7 @@
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="Barrier.qrc"/>
|
<include location="../res/Barrier.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
|
|
@ -16,8 +16,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SslCertificate.h"
|
#include "SslCertificate.h"
|
||||||
#include "Fingerprint.h"
|
|
||||||
#include "common/DataDirectories.h"
|
#include "common/DataDirectories.h"
|
||||||
|
#include "base/finally.h"
|
||||||
|
#include "io/fstream.h"
|
||||||
|
#include "net/FingerprintDatabase.h"
|
||||||
|
#include "net/SecureUtils.h"
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -29,16 +32,8 @@
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
static const char kCertificateLifetime[] = "365";
|
|
||||||
static const char kCertificateSubjectInfo[] = "/CN=Barrier";
|
|
||||||
static const char kCertificateFilename[] = "Barrier.pem";
|
static const char kCertificateFilename[] = "Barrier.pem";
|
||||||
static const char kSslDir[] = "SSL";
|
static const char kSslDir[] = "SSL";
|
||||||
static const char kUnixOpenSslCommand[] = "openssl";
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
static const char kWinOpenSslBinary[] = "openssl.exe";
|
|
||||||
static const char kConfigFile[] = "barrier.conf";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SslCertificate::SslCertificate(QObject *parent) :
|
SslCertificate::SslCertificate(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
|
@ -49,134 +44,46 @@ SslCertificate::SslCertificate(QObject *parent) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, std::string> SslCertificate::runTool(const QStringList& args)
|
|
||||||
{
|
|
||||||
QString program;
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
program = QCoreApplication::applicationDirPath();
|
|
||||||
program.append("\\").append(kWinOpenSslBinary);
|
|
||||||
#else
|
|
||||||
program = kUnixOpenSslCommand;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
QStringList environment;
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
environment << QString("OPENSSL_CONF=%1\\%2")
|
|
||||||
.arg(QCoreApplication::applicationDirPath())
|
|
||||||
.arg(kConfigFile);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QProcess process;
|
|
||||||
process.setEnvironment(environment);
|
|
||||||
process.start(program, args);
|
|
||||||
|
|
||||||
bool success = process.waitForStarted();
|
|
||||||
std::string output;
|
|
||||||
|
|
||||||
QString standardError;
|
|
||||||
if (success && process.waitForFinished())
|
|
||||||
{
|
|
||||||
output = process.readAllStandardOutput().trimmed().toStdString();
|
|
||||||
standardError = process.readAllStandardError().trimmed();
|
|
||||||
}
|
|
||||||
|
|
||||||
int code = process.exitCode();
|
|
||||||
if (!success || code != 0)
|
|
||||||
{
|
|
||||||
emit error(
|
|
||||||
QString("SSL tool failed: %1\n\nCode: %2\nError: %3")
|
|
||||||
.arg(program)
|
|
||||||
.arg(process.exitCode())
|
|
||||||
.arg(standardError.isEmpty() ? "Unknown" : standardError));
|
|
||||||
return {false, output};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {true, output};
|
|
||||||
}
|
|
||||||
|
|
||||||
void SslCertificate::generateCertificate()
|
void SslCertificate::generateCertificate()
|
||||||
{
|
{
|
||||||
auto filename = QString::fromStdString(getCertificatePath());
|
auto cert_path = getCertificatePath();
|
||||||
|
|
||||||
QFile file(filename);
|
|
||||||
if (!file.exists() || !isCertificateValid(filename)) {
|
|
||||||
QStringList arguments;
|
|
||||||
|
|
||||||
// self signed certificate
|
|
||||||
arguments.append("req");
|
|
||||||
arguments.append("-x509");
|
|
||||||
arguments.append("-nodes");
|
|
||||||
|
|
||||||
// valid duration
|
|
||||||
arguments.append("-days");
|
|
||||||
arguments.append(kCertificateLifetime);
|
|
||||||
|
|
||||||
// subject information
|
|
||||||
arguments.append("-subj");
|
|
||||||
|
|
||||||
QString subInfo(kCertificateSubjectInfo);
|
|
||||||
arguments.append(subInfo);
|
|
||||||
|
|
||||||
// private key
|
|
||||||
arguments.append("-newkey");
|
|
||||||
arguments.append("rsa:2048");
|
|
||||||
|
|
||||||
|
QFile file(QString::fromStdString(cert_path));
|
||||||
|
if (!file.exists() || !isCertificateValid(cert_path)) {
|
||||||
QDir sslDir(QString::fromStdString(getCertificateDirectory()));
|
QDir sslDir(QString::fromStdString(getCertificateDirectory()));
|
||||||
if (!sslDir.exists()) {
|
if (!sslDir.exists()) {
|
||||||
sslDir.mkpath(".");
|
sslDir.mkpath(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
// key output filename
|
try {
|
||||||
arguments.append("-keyout");
|
barrier::generate_pem_self_signed_cert(cert_path);
|
||||||
arguments.append(filename);
|
} catch (const std::exception& e) {
|
||||||
|
emit error(QString("SSL tool failed: %1").arg(e.what()));
|
||||||
// certificate output filename
|
|
||||||
arguments.append("-out");
|
|
||||||
arguments.append(filename);
|
|
||||||
|
|
||||||
if (!runTool(arguments).first) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit info(tr("SSL certificate generated."));
|
emit info(tr("SSL certificate generated."));
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFingerprint(filename);
|
generateFingerprint(cert_path);
|
||||||
|
|
||||||
emit generateFinished();
|
emit generateFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SslCertificate::generateFingerprint(const QString& certificateFilename)
|
void SslCertificate::generateFingerprint(const std::string& cert_path)
|
||||||
{
|
{
|
||||||
QStringList arguments;
|
try {
|
||||||
arguments.append("x509");
|
auto local_path = DataDirectories::local_ssl_fingerprints_path();
|
||||||
arguments.append("-fingerprint");
|
barrier::FingerprintDatabase db;
|
||||||
arguments.append("-sha1");
|
db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path,
|
||||||
arguments.append("-noout");
|
barrier::FingerprintType::SHA1));
|
||||||
arguments.append("-in");
|
db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path,
|
||||||
arguments.append(certificateFilename);
|
barrier::FingerprintType::SHA256));
|
||||||
|
db.write(local_path);
|
||||||
|
|
||||||
auto ret = runTool(arguments);
|
|
||||||
bool success = ret.first;
|
|
||||||
std::string output = ret.second;
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the fingerprint from the tool output
|
|
||||||
auto i = output.find_first_of('=');
|
|
||||||
if (i != std::string::npos) {
|
|
||||||
i++;
|
|
||||||
auto fingerprint = output.substr(
|
|
||||||
i, output.size() - i);
|
|
||||||
|
|
||||||
Fingerprint::local().trust(QString::fromStdString(fingerprint), false);
|
|
||||||
emit info(tr("SSL fingerprint generated."));
|
emit info(tr("SSL fingerprint generated."));
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
else {
|
emit error(tr("Failed to find SSL fingerprint.") + e.what());
|
||||||
emit error(tr("Failed to find SSL fingerprint."));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,42 +97,35 @@ std::string SslCertificate::getCertificateDirectory()
|
||||||
return m_ProfileDir + QDir::separator().toLatin1() + kSslDir;
|
return m_ProfileDir + QDir::separator().toLatin1() + kSslDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SslCertificate::isCertificateValid(const QString& path)
|
bool SslCertificate::isCertificateValid(const std::string& path)
|
||||||
{
|
{
|
||||||
OpenSSL_add_all_algorithms();
|
OpenSSL_add_all_algorithms();
|
||||||
ERR_load_BIO_strings();
|
|
||||||
ERR_load_crypto_strings();
|
ERR_load_crypto_strings();
|
||||||
|
|
||||||
BIO* bio = BIO_new(BIO_s_file());
|
auto fp = barrier::fopen_utf8_path(path, "r");
|
||||||
|
if (!fp) {
|
||||||
auto ret = BIO_read_filename(bio, path.toStdString().c_str());
|
|
||||||
if (!ret) {
|
|
||||||
emit info(tr("Could not read from default certificate file."));
|
emit info(tr("Could not read from default certificate file."));
|
||||||
BIO_free_all(bio);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
auto file_close = barrier::finally([fp]() { std::fclose(fp); });
|
||||||
|
|
||||||
X509* cert = PEM_read_bio_X509(bio, NULL, 0, NULL);
|
auto* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr);
|
||||||
if (!cert) {
|
if (!cert) {
|
||||||
emit info(tr("Error loading default certificate file to memory."));
|
emit info(tr("Error loading default certificate file to memory."));
|
||||||
BIO_free_all(bio);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
auto cert_free = barrier::finally([cert]() { X509_free(cert); });
|
||||||
|
|
||||||
EVP_PKEY* pubkey = X509_get_pubkey(cert);
|
auto* pubkey = X509_get_pubkey(cert);
|
||||||
if (!pubkey) {
|
if (!pubkey) {
|
||||||
emit info(tr("Default certificate key file does not contain valid public key"));
|
emit info(tr("Default certificate key file does not contain valid public key"));
|
||||||
X509_free(cert);
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
auto pubkey_free = barrier::finally([pubkey]() { EVP_PKEY_free(pubkey); });
|
||||||
|
|
||||||
auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey));
|
auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey));
|
||||||
if (type != EVP_PKEY_RSA && type != EVP_PKEY_DSA) {
|
if (type != EVP_PKEY_RSA && type != EVP_PKEY_DSA) {
|
||||||
emit info(tr("Public key in default certificate key file is not RSA or DSA"));
|
emit info(tr("Public key in default certificate key file is not RSA or DSA"));
|
||||||
EVP_PKEY_free(pubkey);
|
|
||||||
X509_free(cert);
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,14 +133,8 @@ bool SslCertificate::isCertificateValid(const QString& path)
|
||||||
if (bits < 2048) {
|
if (bits < 2048) {
|
||||||
// We could have small keys in old barrier installations
|
// We could have small keys in old barrier installations
|
||||||
emit info(tr("Public key in default certificate key file is too small."));
|
emit info(tr("Public key in default certificate key file is too small."));
|
||||||
EVP_PKEY_free(pubkey);
|
|
||||||
X509_free(cert);
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EVP_PKEY_free(pubkey);
|
|
||||||
X509_free(cert);
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,12 +37,12 @@ signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pair<bool, std::string> runTool(const QStringList& args);
|
std::pair<bool, std::string> runTool(const QStringList& args);
|
||||||
void generateFingerprint(const QString& certificateFilename);
|
void generateFingerprint(const std::string& cert_path);
|
||||||
|
|
||||||
std::string getCertificatePath();
|
std::string getCertificatePath();
|
||||||
std::string getCertificateDirectory();
|
std::string getCertificateDirectory();
|
||||||
|
|
||||||
bool isCertificateValid(const QString& path);
|
bool isCertificateValid(const std::string& path);
|
||||||
private:
|
private:
|
||||||
std::string m_ProfileDir;
|
std::string m_ProfileDir;
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,16 +46,6 @@ public:
|
||||||
//! @name manipulators
|
//! @name manipulators
|
||||||
//@{
|
//@{
|
||||||
|
|
||||||
//! printf() to limited size buffer with va_list
|
|
||||||
/*!
|
|
||||||
This method is equivalent to vsprintf() except it will not write
|
|
||||||
more than \c n bytes to the buffer, returning -1 if the output
|
|
||||||
was truncated and the number of bytes written not including the
|
|
||||||
trailing NUL otherwise.
|
|
||||||
*/
|
|
||||||
virtual int vsnprintf(char* str,
|
|
||||||
int size, const char* fmt, va_list ap);
|
|
||||||
|
|
||||||
//! Convert multibyte string to wide character string
|
//! Convert multibyte string to wide character string
|
||||||
virtual int convStringMBToWC(wchar_t*,
|
virtual int convStringMBToWC(wchar_t*,
|
||||||
const char*, UInt32 n, bool* errors);
|
const char*, UInt32 n, bool* errors);
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "arch/multibyte.h"
|
#include "arch/multibyte.h"
|
||||||
#include "arch/vsnprintf.h"
|
|
||||||
|
|
||||||
ArchStringUnix::ArchStringUnix()
|
ArchStringUnix::ArchStringUnix()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* barrier -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* Copyright (C) 2002 Chris Schoeneman
|
|
||||||
*
|
|
||||||
* This package is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* found in the file LICENSE that should have accompanied this file.
|
|
||||||
*
|
|
||||||
* This package 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 "arch/IArchString.h"
|
|
||||||
|
|
||||||
#if HAVE_VSNPRINTF
|
|
||||||
|
|
||||||
#if !defined(ARCH_VSNPRINTF)
|
|
||||||
# define ARCH_VSNPRINTF vsnprintf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
|
||||||
IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
|
|
||||||
{
|
|
||||||
int n = ::ARCH_VSNPRINTF(str, size, fmt, ap);
|
|
||||||
if (n > size) {
|
|
||||||
n = -1;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif SYSAPI_UNIX // !HAVE_VSNPRINTF
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int
|
|
||||||
IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
|
|
||||||
{
|
|
||||||
static FILE* bitbucket = fopen("/dev/null", "w");
|
|
||||||
if (bitbucket == NULL) {
|
|
||||||
// uh oh
|
|
||||||
if (size > 0) {
|
|
||||||
str[0] = '\0';
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// count the characters using the bitbucket
|
|
||||||
int n = vfprintf(bitbucket, fmt, ap);
|
|
||||||
if (n + 1 <= size) {
|
|
||||||
// it'll fit so print it into str
|
|
||||||
vsprintf(str, fmt, ap);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX
|
|
||||||
|
|
||||||
#error vsnprintf not implemented
|
|
||||||
|
|
||||||
#endif // !HAVE_VSNPRINTF
|
|
|
@ -26,11 +26,6 @@
|
||||||
// ArchStringWindows
|
// ArchStringWindows
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "arch/multibyte.h"
|
|
||||||
#define HAVE_VSNPRINTF 1
|
|
||||||
#define ARCH_VSNPRINTF _vsnprintf
|
|
||||||
#include "arch/vsnprintf.h"
|
|
||||||
|
|
||||||
ArchStringWindows::ArchStringWindows()
|
ArchStringWindows::ArchStringWindows()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ Log::print(const char* file, int line, const char* fmt, ...)
|
||||||
// try printing into the buffer
|
// try printing into the buffer
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args);
|
int n = std::vsnprintf(buffer, len - sPad, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
// if the buffer wasn't big enough then make it bigger and try again
|
// if the buffer wasn't big enough then make it bigger and try again
|
||||||
|
|
|
@ -35,6 +35,42 @@
|
||||||
namespace barrier {
|
namespace barrier {
|
||||||
namespace string {
|
namespace string {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// returns negative in case of non-matching character
|
||||||
|
int hex_to_number(char ch)
|
||||||
|
{
|
||||||
|
switch (ch) {
|
||||||
|
case '0': return 0;
|
||||||
|
case '1': return 1;
|
||||||
|
case '2': return 2;
|
||||||
|
case '3': return 3;
|
||||||
|
case '4': return 4;
|
||||||
|
case '5': return 5;
|
||||||
|
case '6': return 6;
|
||||||
|
case '7': return 7;
|
||||||
|
case '8': return 8;
|
||||||
|
case '9': return 9;
|
||||||
|
|
||||||
|
case 'a': return 10;
|
||||||
|
case 'b': return 11;
|
||||||
|
case 'c': return 12;
|
||||||
|
case 'd': return 13;
|
||||||
|
case 'e': return 14;
|
||||||
|
case 'f': return 15;
|
||||||
|
|
||||||
|
case 'A': return 10;
|
||||||
|
case 'B': return 11;
|
||||||
|
case 'C': return 12;
|
||||||
|
case 'D': return 13;
|
||||||
|
case 'E': return 14;
|
||||||
|
case 'F': return 15;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
format(const char* fmt, ...)
|
format(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -135,7 +171,7 @@ sprintf(const char* fmt, ...)
|
||||||
// try printing into the buffer
|
// try printing into the buffer
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
int n = ARCH->vsnprintf(buffer, len, fmt, args);
|
int n = std::vsnprintf(buffer, len, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
// if the buffer wasn't big enough then make it bigger and try again
|
// if the buffer wasn't big enough then make it bigger and try again
|
||||||
|
@ -185,16 +221,42 @@ removeFileExt(std::string filename)
|
||||||
return filename.substr(0, dot);
|
return filename.substr(0, dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
std::string to_hex(const std::vector<std::uint8_t>& subject, int width, const char fill)
|
||||||
toHex(std::string& subject, int width, const char fill)
|
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::hex;
|
ss << std::hex;
|
||||||
for (unsigned int i = 0; i < subject.length(); i++) {
|
for (unsigned int i = 0; i < subject.size(); i++) {
|
||||||
ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i];
|
ss << std::setw(width) << std::setfill(fill) << static_cast<int>(subject[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
subject = ss.str();
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> from_hex(const std::string& data)
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> result;
|
||||||
|
result.reserve(data.size() / 2);
|
||||||
|
|
||||||
|
std::size_t i = 0;
|
||||||
|
while (i < data.size()) {
|
||||||
|
if (data[i] == ':') {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 2 > data.size()) {
|
||||||
|
return {}; // uneven character count follows, it's unclear how to interpret it
|
||||||
|
}
|
||||||
|
|
||||||
|
auto high = hex_to_number(data[i]);
|
||||||
|
auto low = hex_to_number(data[i + 1]);
|
||||||
|
if (high < 0 || low < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
result.push_back(high * 16 + low);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -75,7 +75,10 @@ std::string removeFileExt(std::string filename);
|
||||||
/*!
|
/*!
|
||||||
Convert each character in \c subject into hexdecimal form with \c width
|
Convert each character in \c subject into hexdecimal form with \c width
|
||||||
*/
|
*/
|
||||||
void toHex(std::string& subject, int width, const char fill = '0');
|
std::string to_hex(const std::vector<std::uint8_t>& subject, int width, const char fill = '0');
|
||||||
|
|
||||||
|
/// Convert binary data from hexadecimal
|
||||||
|
std::vector<std::uint8_t> from_hex(const std::string& data);
|
||||||
|
|
||||||
//! Convert to all uppercase
|
//! Convert to all uppercase
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 BARRIER_LIB_BASE_FINALLY_H
|
||||||
|
#define BARRIER_LIB_BASE_FINALLY_H
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
// this implements a common pattern of executing an action at the end of function
|
||||||
|
|
||||||
|
template<class Callable>
|
||||||
|
class final_action {
|
||||||
|
public:
|
||||||
|
final_action() noexcept {}
|
||||||
|
final_action(Callable callable) noexcept : callable_{callable} {}
|
||||||
|
|
||||||
|
~final_action() noexcept
|
||||||
|
{
|
||||||
|
if (!invoked_) {
|
||||||
|
callable_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final_action(final_action&& other) noexcept :
|
||||||
|
callable_{std::move(other.callable_)}
|
||||||
|
{
|
||||||
|
std::swap(invoked_, other.invoked_);
|
||||||
|
}
|
||||||
|
|
||||||
|
final_action(const final_action&) = delete;
|
||||||
|
final_action& operator=(const final_action&) = delete;
|
||||||
|
private:
|
||||||
|
bool invoked_ = false;
|
||||||
|
Callable callable_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Callable>
|
||||||
|
inline final_action<Callable> finally(Callable&& callable) noexcept
|
||||||
|
{
|
||||||
|
return final_action<Callable>(std::forward<Callable>(callable));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace barrier
|
||||||
|
|
||||||
|
#endif // BARRIER_LIB_BASE_FINALLY_H
|
|
@ -31,10 +31,11 @@ public:
|
||||||
static const std::string& systemconfig();
|
static const std::string& systemconfig();
|
||||||
static const std::string& systemconfig(const std::string& path);
|
static const std::string& systemconfig(const std::string& path);
|
||||||
|
|
||||||
|
static std::string ssl_fingerprints_path();
|
||||||
|
static std::string local_ssl_fingerprints_path();
|
||||||
|
static std::string trusted_servers_ssl_fingerprints_path();
|
||||||
|
static std::string trusted_clients_ssl_fingerprints_path();
|
||||||
private:
|
private:
|
||||||
// static class
|
|
||||||
DataDirectories() {}
|
|
||||||
|
|
||||||
static std::string _profile;
|
static std::string _profile;
|
||||||
static std::string _global;
|
static std::string _global;
|
||||||
static std::string _systemconfig;
|
static std::string _systemconfig;
|
||||||
|
|
|
@ -21,3 +21,28 @@
|
||||||
std::string DataDirectories::_profile;
|
std::string DataDirectories::_profile;
|
||||||
std::string DataDirectories::_global;
|
std::string DataDirectories::_global;
|
||||||
std::string DataDirectories::_systemconfig;
|
std::string DataDirectories::_systemconfig;
|
||||||
|
|
||||||
|
static const char kFingerprintsDirName[] = "SSL/Fingerprints";
|
||||||
|
static const char kFingerprintsLocalFilename[] = "Local.txt";
|
||||||
|
static const char kFingerprintsTrustedServersFilename[] = "TrustedServers.txt";
|
||||||
|
static const char kFingerprintsTrustedClientsFilename[] = "TrustedClients.txt";
|
||||||
|
|
||||||
|
std::string DataDirectories::ssl_fingerprints_path()
|
||||||
|
{
|
||||||
|
return profile() + "/" + kFingerprintsDirName;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DataDirectories::local_ssl_fingerprints_path()
|
||||||
|
{
|
||||||
|
return ssl_fingerprints_path() + "/" + kFingerprintsLocalFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DataDirectories::trusted_servers_ssl_fingerprints_path()
|
||||||
|
{
|
||||||
|
return ssl_fingerprints_path() + "/" + kFingerprintsTrustedServersFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DataDirectories::trusted_clients_ssl_fingerprints_path()
|
||||||
|
{
|
||||||
|
return ssl_fingerprints_path() + "/" + kFingerprintsTrustedClientsFilename;
|
||||||
|
}
|
||||||
|
|
|
@ -24,8 +24,4 @@ class PathUtilities
|
||||||
public:
|
public:
|
||||||
static std::string basename(const std::string& path);
|
static std::string basename(const std::string& path);
|
||||||
static std::string concat(const std::string& left, const std::string& right);
|
static std::string concat(const std::string& left, const std::string& right);
|
||||||
|
|
||||||
private:
|
|
||||||
// static class
|
|
||||||
PathUtilities() {}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,4 +54,16 @@ void open_utf8_path(std::fstream& stream, const std::string& path, std::ios_base
|
||||||
open_utf8_path_impl(stream, path, mode);
|
open_utf8_path_impl(stream, path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::FILE* fopen_utf8_path(const std::string& path, const std::string& mode)
|
||||||
|
{
|
||||||
|
#if SYSAPI_WIN32
|
||||||
|
auto wchar_path = utf8_to_win_char(path);
|
||||||
|
auto wchar_mode = utf8_to_win_char(mode);
|
||||||
|
return _wfopen(reinterpret_cast<wchar_t*>(wchar_path.data()),
|
||||||
|
reinterpret_cast<wchar_t*>(wchar_mode.data()));
|
||||||
|
#else
|
||||||
|
return std::fopen(path.c_str(), mode.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace barrier
|
} // namespace barrier
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#ifndef BARRIER_LIB_IO_FSTREAM_H
|
#ifndef BARRIER_LIB_IO_FSTREAM_H
|
||||||
#define BARRIER_LIB_IO_FSTREAM_H
|
#define BARRIER_LIB_IO_FSTREAM_H
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <ios>
|
#include <ios>
|
||||||
|
|
||||||
|
@ -30,6 +31,8 @@ void open_utf8_path(std::ofstream& stream, const std::string& path,
|
||||||
void open_utf8_path(std::fstream& stream, const std::string& path,
|
void open_utf8_path(std::fstream& stream, const std::string& path,
|
||||||
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out);
|
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out);
|
||||||
|
|
||||||
|
std::FILE* fopen_utf8_path(const std::string& path, const std::string& mode);
|
||||||
|
|
||||||
} // namespace barrier
|
} // namespace barrier
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 "base/String.h"
|
||||||
|
#include "FingerprintDatabase.h"
|
||||||
|
#include "io/fstream.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
bool FingerprintData::operator==(const FingerprintData& other) const
|
||||||
|
{
|
||||||
|
return algorithm == other.algorithm && data == other.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* fingerprint_type_to_string(FingerprintType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case FingerprintType::INVALID: return "invalid";
|
||||||
|
case FingerprintType::SHA1: return "sha1";
|
||||||
|
case FingerprintType::SHA256: return "sha256";
|
||||||
|
}
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerprintType fingerprint_type_from_string(const std::string& type)
|
||||||
|
{
|
||||||
|
if (type == "sha1") {
|
||||||
|
return FingerprintType::SHA1;
|
||||||
|
}
|
||||||
|
if (type == "sha256") {
|
||||||
|
return FingerprintType::SHA256;
|
||||||
|
}
|
||||||
|
return FingerprintType::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace barrier
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 BARRIER_LIB_NET_FINGERPRINT_DATA_H
|
||||||
|
#define BARRIER_LIB_NET_FINGERPRINT_DATA_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
enum FingerprintType {
|
||||||
|
INVALID,
|
||||||
|
SHA1, // deprecated
|
||||||
|
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* fingerprint_type_to_string(FingerprintType type);
|
||||||
|
FingerprintType fingerprint_type_from_string(const std::string& type);
|
||||||
|
|
||||||
|
} // namespace barrier
|
||||||
|
|
||||||
|
#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 "base/String.h"
|
||||||
|
#include "FingerprintDatabase.h"
|
||||||
|
#include "io/fstream.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
void FingerprintDatabase::read(const std::string& path)
|
||||||
|
{
|
||||||
|
std::ifstream file;
|
||||||
|
open_utf8_path(file, path, std::ios_base::in);
|
||||||
|
read_stream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FingerprintDatabase::write(const std::string& path)
|
||||||
|
{
|
||||||
|
std::ofstream file;
|
||||||
|
open_utf8_path(file, path, std::ios_base::out);
|
||||||
|
write_stream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FingerprintDatabase::read_stream(std::istream& stream)
|
||||||
|
{
|
||||||
|
if (!stream.good()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(stream, line)) {
|
||||||
|
if (line.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fingerprint = parse_db_line(line);
|
||||||
|
if (!fingerprint.valid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fingerprints_.push_back(fingerprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FingerprintDatabase::write_stream(std::ostream& stream)
|
||||||
|
{
|
||||||
|
if (!stream.good()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& fingerprint : fingerprints_) {
|
||||||
|
stream << to_db_line(fingerprint) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FingerprintDatabase::clear()
|
||||||
|
{
|
||||||
|
fingerprints_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FingerprintDatabase::add_trusted(const FingerprintData& fingerprint)
|
||||||
|
{
|
||||||
|
if (is_trusted(fingerprint)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fingerprints_.push_back(fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FingerprintDatabase::is_trusted(const FingerprintData& fingerprint)
|
||||||
|
{
|
||||||
|
auto found_it = std::find(fingerprints_.begin(), fingerprints_.end(), fingerprint);
|
||||||
|
return found_it != fingerprints_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerprintData FingerprintDatabase::parse_db_line(const std::string& line)
|
||||||
|
{
|
||||||
|
FingerprintData result;
|
||||||
|
|
||||||
|
// legacy v1 certificate handling
|
||||||
|
if (std::count(line.begin(), line.end(), ':') == 19 && line.size() == 40 + 19) {
|
||||||
|
auto data = string::from_hex(line);
|
||||||
|
if (data.empty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.algorithm = fingerprint_type_to_string(FingerprintType::SHA1);
|
||||||
|
result.data = data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto version_end_pos = line.find(':');
|
||||||
|
if (version_end_pos == std::string::npos) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (line.substr(0, version_end_pos) != "v2") {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
auto algo_start_pos = version_end_pos + 1;
|
||||||
|
auto algo_end_pos = line.find(':', algo_start_pos);
|
||||||
|
if (algo_end_pos == std::string::npos) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
auto algorithm = line.substr(algo_start_pos, algo_end_pos - algo_start_pos);
|
||||||
|
auto data = string::from_hex(line.substr(algo_end_pos + 1));
|
||||||
|
|
||||||
|
if (data.empty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.algorithm = algorithm;
|
||||||
|
result.data = data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FingerprintDatabase::to_db_line(const FingerprintData& fingerprint)
|
||||||
|
{
|
||||||
|
return "v2:" + fingerprint.algorithm + ":" + string::to_hex(fingerprint.data, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace barrier
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 BARRIER_LIB_NET_FINGERPRINT_DATABASE_H
|
||||||
|
#define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H
|
||||||
|
|
||||||
|
#include "FingerprintData.h"
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
class FingerprintDatabase {
|
||||||
|
public:
|
||||||
|
void read(const std::string& path);
|
||||||
|
void write(const std::string& path);
|
||||||
|
|
||||||
|
void read_stream(std::istream& stream);
|
||||||
|
void write_stream(std::ostream& stream);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void add_trusted(const FingerprintData& fingerprint);
|
||||||
|
bool is_trusted(const FingerprintData& fingerprint);
|
||||||
|
|
||||||
|
const std::vector<FingerprintData>& fingerprints() const { return fingerprints_; }
|
||||||
|
|
||||||
|
static FingerprintData parse_db_line(const std::string& line);
|
||||||
|
static std::string to_db_line(const FingerprintData& fingerprint);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<FingerprintData> fingerprints_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace barrier
|
||||||
|
|
||||||
|
#endif // BARRIER_LIB_NET_FINGERPRINT_DATABASE_H
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SecureSocket.h"
|
#include "SecureSocket.h"
|
||||||
|
#include "SecureUtils.h"
|
||||||
|
|
||||||
#include "net/TSocketMultiplexerMethodJob.h"
|
#include "net/TSocketMultiplexerMethodJob.h"
|
||||||
#include "base/TMethodEventJob.h"
|
#include "base/TMethodEventJob.h"
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
#include "base/String.h"
|
#include "base/String.h"
|
||||||
#include "common/DataDirectories.h"
|
#include "common/DataDirectories.h"
|
||||||
#include "io/fstream.h"
|
#include "io/fstream.h"
|
||||||
|
#include "net/FingerprintDatabase.h"
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
@ -47,11 +49,6 @@ enum {
|
||||||
kMsgSize = 128
|
kMsgSize = 128
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char kFingerprintDirName[] = "SSL/Fingerprints";
|
|
||||||
//static const char kFingerprintLocalFilename[] = "Local.txt";
|
|
||||||
static const char kFingerprintTrustedServersFilename[] = "TrustedServers.txt";
|
|
||||||
//static const char kFingerprintTrustedClientsFilename[] = "TrustedClients.txt";
|
|
||||||
|
|
||||||
struct Ssl {
|
struct Ssl {
|
||||||
SSL_CTX* m_context;
|
SSL_CTX* m_context;
|
||||||
SSL* m_ssl;
|
SSL* m_ssl;
|
||||||
|
@ -656,84 +653,51 @@ SecureSocket::disconnect()
|
||||||
sendEvent(getEvents()->forIStream().inputShutdown());
|
sendEvent(getEvents()->forIStream().inputShutdown());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecureSocket::formatFingerprint(std::string& fingerprint, bool hex, bool separator)
|
|
||||||
{
|
|
||||||
if (hex) {
|
|
||||||
// to hexadecimal
|
|
||||||
barrier::string::toHex(fingerprint, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// all uppercase
|
|
||||||
barrier::string::uppercase(fingerprint);
|
|
||||||
|
|
||||||
if (separator) {
|
|
||||||
// add colon to separate each 2 characters
|
|
||||||
size_t separators = fingerprint.size() / 2;
|
|
||||||
for (size_t i = 1; i < separators; i++) {
|
|
||||||
fingerprint.insert(i * 3 - 1, ":");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SecureSocket::verifyCertFingerprint()
|
SecureSocket::verifyCertFingerprint()
|
||||||
{
|
{
|
||||||
// calculate received certificate fingerprint
|
// calculate received certificate fingerprint
|
||||||
X509 *cert = cert = SSL_get_peer_certificate(m_ssl->m_ssl);
|
barrier::FingerprintData fingerprint_sha1, fingerprint_sha256;
|
||||||
EVP_MD* tempDigest;
|
try {
|
||||||
unsigned char tempFingerprint[EVP_MAX_MD_SIZE];
|
auto* cert = SSL_get_peer_certificate(m_ssl->m_ssl);
|
||||||
unsigned int tempFingerprintLen;
|
fingerprint_sha1 = barrier::get_ssl_cert_fingerprint(cert,
|
||||||
tempDigest = (EVP_MD*)EVP_sha1();
|
barrier::FingerprintType::SHA1);
|
||||||
int digestResult = X509_digest(cert, tempDigest, tempFingerprint, &tempFingerprintLen);
|
fingerprint_sha256 = barrier::get_ssl_cert_fingerprint(cert,
|
||||||
|
barrier::FingerprintType::SHA256);
|
||||||
if (digestResult <= 0) {
|
} catch (const std::exception& e) {
|
||||||
LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult));
|
LOG((CLOG_ERR "%s", e.what()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// format fingerprint into hexdecimal format with colon separator
|
// note: the GUI parses the following two lines of logs, don't change unnecessarily
|
||||||
std::string fingerprint(reinterpret_cast<char*>(tempFingerprint), tempFingerprintLen);
|
LOG((CLOG_NOTE "server fingerprint (SHA1): %s (SHA256): %s",
|
||||||
formatFingerprint(fingerprint);
|
barrier::format_ssl_fingerprint(fingerprint_sha1.data).c_str(),
|
||||||
LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str()));
|
barrier::format_ssl_fingerprint(fingerprint_sha256.data).c_str()));
|
||||||
|
|
||||||
std::string trustedServersFilename;
|
auto fingerprint_db_path = DataDirectories::trusted_servers_ssl_fingerprints_path();
|
||||||
trustedServersFilename = barrier::string::sprintf(
|
|
||||||
"%s/%s/%s",
|
|
||||||
DataDirectories::profile().c_str(),
|
|
||||||
kFingerprintDirName,
|
|
||||||
kFingerprintTrustedServersFilename);
|
|
||||||
|
|
||||||
// Provide debug hint as to what file is being used to verify fingerprint trust
|
// Provide debug hint as to what file is being used to verify fingerprint trust
|
||||||
LOG((CLOG_NOTE "trustedServersFilename: %s", trustedServersFilename.c_str() ));
|
LOG((CLOG_NOTE "fingerprint_db_path: %s", fingerprint_db_path.c_str()));
|
||||||
|
|
||||||
// check if this fingerprint exist
|
barrier::FingerprintDatabase db;
|
||||||
std::string fileLine;
|
db.read(fingerprint_db_path);
|
||||||
std::ifstream file;
|
|
||||||
barrier::open_utf8_path(file, trustedServersFilename);
|
|
||||||
|
|
||||||
if (!file.is_open()) {
|
if (!db.fingerprints().empty()) {
|
||||||
LOG((CLOG_NOTE "Unable to open trustedServersFile: %s", trustedServersFilename.c_str() ));
|
LOG((CLOG_NOTE "Read %d fingerprints from: %s", db.fingerprints().size(),
|
||||||
|
fingerprint_db_path.c_str()));
|
||||||
} else {
|
} else {
|
||||||
LOG((CLOG_NOTE "Opened trustedServersFilename: %s", trustedServersFilename.c_str() ));
|
LOG((CLOG_NOTE "Could not read fingerprints from: %s",
|
||||||
|
fingerprint_db_path.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValid = false;
|
if (db.is_trusted(fingerprint_sha256)) {
|
||||||
while (!file.eof() && file.is_open()) {
|
|
||||||
getline(file,fileLine);
|
|
||||||
if (!fileLine.empty()) {
|
|
||||||
if (fileLine.compare(fingerprint) == 0) {
|
|
||||||
LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint"));
|
LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint"));
|
||||||
isValid = true;
|
return true;
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint"));
|
LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint"));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
return isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job,
|
MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job,
|
||||||
bool read, bool write, bool error)
|
bool read, bool write, bool error)
|
||||||
|
|
|
@ -68,7 +68,6 @@ private:
|
||||||
void showError(const std::string& reason);
|
void showError(const std::string& reason);
|
||||||
std::string getError();
|
std::string getError();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
void formatFingerprint(std::string& fingerprint, bool hex = true, bool separator = true);
|
|
||||||
bool verifyCertFingerprint();
|
bool verifyCertFingerprint();
|
||||||
|
|
||||||
MultiplexerJobStatus serviceConnect(ISocketMultiplexerJob*, bool, bool, bool);
|
MultiplexerJobStatus serviceConnect(ISocketMultiplexerJob*, bool, bool, bool);
|
||||||
|
|
|
@ -0,0 +1,306 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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/>.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
create_fingerprint_randomart() has been taken from the OpenSSH project.
|
||||||
|
Copyright information follows.
|
||||||
|
|
||||||
|
Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||||
|
Copyright (c) 2008 Alexander von Gernler. All rights reserved.
|
||||||
|
Copyright (c) 2010,2011 Damien Miller. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SecureUtils.h"
|
||||||
|
#include "base/String.h"
|
||||||
|
#include "base/finally.h"
|
||||||
|
#include "io/fstream.h"
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/x509v3.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const EVP_MD* get_digest_for_type(FingerprintType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case FingerprintType::SHA1: return EVP_sha1();
|
||||||
|
case FingerprintType::SHA256: return EVP_sha256();
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Unknown fingerprint type " + std::to_string(static_cast<int>(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::string format_ssl_fingerprint(const std::vector<uint8_t>& fingerprint, bool separator)
|
||||||
|
{
|
||||||
|
std::string result = barrier::string::to_hex(fingerprint, 2);
|
||||||
|
|
||||||
|
// all uppercase
|
||||||
|
barrier::string::uppercase(result);
|
||||||
|
|
||||||
|
if (separator) {
|
||||||
|
// add colon to separate each 2 characters
|
||||||
|
size_t separators = result.size() / 2;
|
||||||
|
for (size_t i = 1; i < separators; i++) {
|
||||||
|
result.insert(i * 3 - 1, ":");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string format_ssl_fingerprint_columns(const std::vector<uint8_t>& fingerprint)
|
||||||
|
{
|
||||||
|
auto max_columns = 8;
|
||||||
|
|
||||||
|
std::string hex = barrier::string::to_hex(fingerprint, 2);
|
||||||
|
barrier::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 += max_columns * 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type)
|
||||||
|
{
|
||||||
|
if (!cert) {
|
||||||
|
throw std::runtime_error("certificate is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char digest[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int digest_length = 0;
|
||||||
|
int result = X509_digest(cert, get_digest_for_type(type), digest, &digest_length);
|
||||||
|
|
||||||
|
if (result <= 0) {
|
||||||
|
throw std::runtime_error("failed to calculate fingerprint, digest result: " +
|
||||||
|
std::to_string(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> digest_vec;
|
||||||
|
digest_vec.assign(reinterpret_cast<std::uint8_t*>(digest),
|
||||||
|
reinterpret_cast<std::uint8_t*>(digest) + digest_length);
|
||||||
|
return {fingerprint_type_to_string(type), digest_vec};
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type)
|
||||||
|
{
|
||||||
|
auto fp = fopen_utf8_path(path, "r");
|
||||||
|
if (!fp) {
|
||||||
|
throw std::runtime_error("Could not open certificate path");
|
||||||
|
}
|
||||||
|
auto file_close = finally([fp]() { std::fclose(fp); });
|
||||||
|
|
||||||
|
X509* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr);
|
||||||
|
if (!cert) {
|
||||||
|
throw std::runtime_error("Certificate could not be parsed");
|
||||||
|
}
|
||||||
|
auto cert_free = finally([cert]() { X509_free(cert); });
|
||||||
|
|
||||||
|
return get_ssl_cert_fingerprint(cert, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_pem_self_signed_cert(const std::string& path)
|
||||||
|
{
|
||||||
|
auto expiration_days = 365;
|
||||||
|
|
||||||
|
auto* private_key = EVP_PKEY_new();
|
||||||
|
if (!private_key) {
|
||||||
|
throw std::runtime_error("Could not allocate private key for certificate");
|
||||||
|
}
|
||||||
|
auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); });
|
||||||
|
|
||||||
|
auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
|
||||||
|
if (!rsa) {
|
||||||
|
throw std::runtime_error("Failed to generate RSA key");
|
||||||
|
}
|
||||||
|
EVP_PKEY_assign_RSA(private_key, rsa);
|
||||||
|
|
||||||
|
auto* cert = X509_new();
|
||||||
|
if (!cert) {
|
||||||
|
throw std::runtime_error("Could not allocate certificate");
|
||||||
|
}
|
||||||
|
auto cert_free = finally([cert]() { X509_free(cert); });
|
||||||
|
|
||||||
|
ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
|
||||||
|
X509_gmtime_adj(X509_get_notBefore(cert), 0);
|
||||||
|
X509_gmtime_adj(X509_get_notAfter(cert), expiration_days * 24 * 3600);
|
||||||
|
X509_set_pubkey(cert, private_key);
|
||||||
|
|
||||||
|
auto* name = X509_get_subject_name(cert);
|
||||||
|
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
|
||||||
|
reinterpret_cast<const unsigned char *>("Barrier"), -1, -1, 0);
|
||||||
|
X509_set_issuer_name(cert, name);
|
||||||
|
|
||||||
|
X509_sign(cert, private_key, EVP_sha256());
|
||||||
|
|
||||||
|
auto fp = fopen_utf8_path(path.c_str(), "r");
|
||||||
|
if (!fp) {
|
||||||
|
throw std::runtime_error("Could not open certificate output path");
|
||||||
|
}
|
||||||
|
auto file_close = finally([fp]() { std::fclose(fp); });
|
||||||
|
|
||||||
|
PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr);
|
||||||
|
PEM_write_X509(fp, cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 dgst_raw 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 FLDBASE 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.
|
||||||
|
*/
|
||||||
|
#define FLDBASE 8
|
||||||
|
#define FLDSIZE_Y (FLDBASE + 1)
|
||||||
|
#define FLDSIZE_X (FLDBASE * 2 + 1)
|
||||||
|
|
||||||
|
std::string create_fingerprint_randomart(const std::vector<std::uint8_t>& dgst_raw)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Chars to be used after each other every time the worm
|
||||||
|
* intersects with itself. Matter of taste.
|
||||||
|
*/
|
||||||
|
const char* augmentation_string = " .o+=*BOX@%&#/^SE";
|
||||||
|
char *p;
|
||||||
|
std::uint8_t field[FLDSIZE_X][FLDSIZE_Y];
|
||||||
|
std::size_t i;
|
||||||
|
std::uint32_t b;
|
||||||
|
int x, y;
|
||||||
|
std::size_t len = strlen(augmentation_string) - 1;
|
||||||
|
|
||||||
|
std::vector<char> retval;
|
||||||
|
retval.reserve((FLDSIZE_X + 3) * (FLDSIZE_Y + 2));
|
||||||
|
|
||||||
|
auto add_char = [&retval](char ch) { retval.push_back(ch); };
|
||||||
|
|
||||||
|
/* initialize field */
|
||||||
|
std::memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
|
||||||
|
x = FLDSIZE_X / 2;
|
||||||
|
y = FLDSIZE_Y / 2;
|
||||||
|
|
||||||
|
/* process raw key */
|
||||||
|
for (i = 0; i < dgst_raw.size(); i++) {
|
||||||
|
/* each byte conveys four 2-bit move commands */
|
||||||
|
int input = dgst_raw[i];
|
||||||
|
for (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::max(x, 0);
|
||||||
|
y = std::max(y, 0);
|
||||||
|
x = std::min(x, FLDSIZE_X - 1);
|
||||||
|
y = std::min(y, FLDSIZE_Y - 1);
|
||||||
|
|
||||||
|
/* augment the field */
|
||||||
|
if (field[x][y] < len - 2)
|
||||||
|
field[x][y]++;
|
||||||
|
input = input >> 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark starting point and end point*/
|
||||||
|
field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
|
||||||
|
field[x][y] = len;
|
||||||
|
|
||||||
|
/* output upper border */
|
||||||
|
add_char('+');
|
||||||
|
for (i = 0; i < FLDSIZE_X; i++)
|
||||||
|
add_char('-');
|
||||||
|
add_char('+');
|
||||||
|
add_char('\n');
|
||||||
|
|
||||||
|
/* output content */
|
||||||
|
for (y = 0; y < FLDSIZE_Y; y++) {
|
||||||
|
add_char('|');
|
||||||
|
for (x = 0; x < FLDSIZE_X; x++)
|
||||||
|
add_char(augmentation_string[std::min<int>(field[x][y], len)]);
|
||||||
|
add_char('|');
|
||||||
|
add_char('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* output lower border */
|
||||||
|
add_char('+');
|
||||||
|
for (i = 0; i < FLDSIZE_X; i++)
|
||||||
|
add_char('-');
|
||||||
|
add_char('+');
|
||||||
|
|
||||||
|
return std::string{retval.data(), retval.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace barrier
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 BARRIER_LIB_NET_SECUREUTILS_H
|
||||||
|
#define BARRIER_LIB_NET_SECUREUTILS_H
|
||||||
|
|
||||||
|
#include "FingerprintData.h"
|
||||||
|
#include <openssl/ossl_typ.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
std::string format_ssl_fingerprint(const std::vector<std::uint8_t>& fingerprint,
|
||||||
|
bool separator = true);
|
||||||
|
std::string format_ssl_fingerprint_columns(const std::vector<uint8_t>& fingerprint);
|
||||||
|
|
||||||
|
FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type);
|
||||||
|
|
||||||
|
FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type);
|
||||||
|
|
||||||
|
void generate_pem_self_signed_cert(const std::string& path);
|
||||||
|
|
||||||
|
std::string create_fingerprint_randomart(const std::vector<std::uint8_t>& dgst_raw);
|
||||||
|
|
||||||
|
} // namespace barrier
|
||||||
|
|
||||||
|
#endif // BARRIER_LIB_NET_SECUREUTILS_H
|
|
@ -27,8 +27,4 @@ class ImmuneKeysReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool get_list(const char * const path, std::vector<DWORD> &keys, std::string &badLine);
|
static bool get_list(const char * const path, std::vector<DWORD> &keys, std::string &badLine);
|
||||||
|
|
||||||
private:
|
|
||||||
// static class
|
|
||||||
explicit ImmuneKeysReader() {}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 "TestUtils.h"
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> generate_pseudo_random_bytes(std::size_t seed, std::size_t size)
|
||||||
|
{
|
||||||
|
std::mt19937_64 engine{seed};
|
||||||
|
std::uniform_int_distribution<int> dist{0, 255};
|
||||||
|
std::vector<std::uint8_t> bytes;
|
||||||
|
|
||||||
|
bytes.reserve(size);
|
||||||
|
for (std::size_t i = 0; i < size; ++i) {
|
||||||
|
bytes.push_back(dist(engine));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace barrier
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 BARRIER_TEST_GLOBAL_TEST_UTILS_H
|
||||||
|
#define BARRIER_TEST_GLOBAL_TEST_UTILS_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> generate_pseudo_random_bytes(std::size_t seed, std::size_t size);
|
||||||
|
|
||||||
|
} // namespace barrier
|
||||||
|
|
||||||
|
#endif // BARRIER_TEST_GLOBAL_TEST_UTILS_H
|
|
@ -56,12 +56,38 @@ TEST(StringTests, sprintf_formatWithArgument_formatedString)
|
||||||
|
|
||||||
TEST(StringTests, toHex_plaintext_hexString)
|
TEST(StringTests, toHex_plaintext_hexString)
|
||||||
{
|
{
|
||||||
String subject = "foobar";
|
std::vector<std::uint8_t> subject{'f', 'o', 'o', 'b', 'a', 'r'};
|
||||||
int width = 2;
|
int width = 2;
|
||||||
|
|
||||||
string::toHex(subject, width);
|
EXPECT_EQ("666f6f626172", string::to_hex(subject, width));
|
||||||
|
}
|
||||||
|
|
||||||
EXPECT_EQ("666f6f626172", subject);
|
TEST(StringTests, fromhex_plaintext_string)
|
||||||
|
{
|
||||||
|
auto result = string::from_hex("666f6f626172");
|
||||||
|
std::string expected = "foobar";
|
||||||
|
EXPECT_EQ(result, std::vector<std::uint8_t>(expected.begin(), expected.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringTests, fromhex_plaintext_string_colons)
|
||||||
|
{
|
||||||
|
auto result = string::from_hex("66:6f:6f:62:61:72");
|
||||||
|
std::string expected = "foobar";
|
||||||
|
EXPECT_EQ(result, std::vector<std::uint8_t>(expected.begin(), expected.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringTests, fromhex_binary_string)
|
||||||
|
{
|
||||||
|
auto result = string::from_hex("01020304050600fff9");
|
||||||
|
auto expected = std::vector<std::uint8_t>{1, 2, 3, 4, 5, 6, 0, 0xff, 0xf9};
|
||||||
|
EXPECT_EQ(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringTests, fromhex_invalid_string)
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(string::from_hex("66:6").empty());
|
||||||
|
EXPECT_TRUE(string::from_hex("66:612").empty());
|
||||||
|
EXPECT_TRUE(string::from_hex("66:WW").empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput)
|
TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput)
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 "net/FingerprintDatabase.h"
|
||||||
|
#include "test/global/gtest.h"
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
TEST(FingerprintDatabase, parse_db_line)
|
||||||
|
{
|
||||||
|
ASSERT_FALSE(FingerprintDatabase::parse_db_line("").valid());
|
||||||
|
ASSERT_FALSE(FingerprintDatabase::parse_db_line("abcd").valid());
|
||||||
|
ASSERT_FALSE(FingerprintDatabase::parse_db_line("v1:algo:something").valid());
|
||||||
|
ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:something").valid());
|
||||||
|
ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304abc").valid());
|
||||||
|
ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304ZZ").valid());
|
||||||
|
ASSERT_EQ(FingerprintDatabase::parse_db_line("v2:algo:01020304ab"),
|
||||||
|
(FingerprintData{"algo", {1, 2, 3, 4, 0xab}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FingerprintDatabase, read)
|
||||||
|
{
|
||||||
|
std::istringstream stream;
|
||||||
|
stream.str(R"(
|
||||||
|
v2:algo1:01020304ab
|
||||||
|
v2:algo2:03040506ab
|
||||||
|
AB:CD:EF:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16
|
||||||
|
)");
|
||||||
|
FingerprintDatabase db;
|
||||||
|
db.read_stream(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 } },
|
||||||
|
};
|
||||||
|
ASSERT_EQ(db.fingerprints(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FingerprintDatabase, write)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
FingerprintDatabase db;
|
||||||
|
db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } });
|
||||||
|
db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } });
|
||||||
|
db.write_stream(stream);
|
||||||
|
|
||||||
|
ASSERT_EQ(stream.str(), R"(v2:algo1:01020304ab
|
||||||
|
v2:algo2:03040506ab
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FingerprintDatabase, clear)
|
||||||
|
{
|
||||||
|
FingerprintDatabase db;
|
||||||
|
db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } });
|
||||||
|
db.clear();
|
||||||
|
ASSERT_TRUE(db.fingerprints().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FingerprintDatabase, add_trusted_no_duplicates)
|
||||||
|
{
|
||||||
|
FingerprintDatabase db;
|
||||||
|
db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } });
|
||||||
|
db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } });
|
||||||
|
db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } });
|
||||||
|
ASSERT_EQ(db.fingerprints().size(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FingerprintDatabase, is_trusted)
|
||||||
|
{
|
||||||
|
FingerprintDatabase db;
|
||||||
|
db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } });
|
||||||
|
ASSERT_TRUE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }));
|
||||||
|
ASSERT_FALSE(db.is_trusted({ "algo2", { 1, 2, 3, 4, 0xab } }));
|
||||||
|
ASSERT_FALSE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xac } }));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace barrier
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
barrier -- mouse and keyboard sharing utility
|
||||||
|
Copyright (C) 2021 Barrier contributors
|
||||||
|
|
||||||
|
This package is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
found in the file LICENSE that should have accompanied this file.
|
||||||
|
|
||||||
|
This package 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 "net/SecureUtils.h"
|
||||||
|
|
||||||
|
#include "test/global/gtest.h"
|
||||||
|
#include "test/global/TestUtils.h"
|
||||||
|
|
||||||
|
namespace barrier {
|
||||||
|
|
||||||
|
TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators)
|
||||||
|
{
|
||||||
|
auto fingerprint = generate_pseudo_random_bytes(0, 32);
|
||||||
|
ASSERT_EQ(format_ssl_fingerprint(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");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SecureUtilsTest, CreateFingerprintRandomArt)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(0, 32)),
|
||||||
|
"+-----------------+\n"
|
||||||
|
"|*X+. . |\n"
|
||||||
|
"|*oo + |\n"
|
||||||
|
"| + = |\n"
|
||||||
|
"| B . . |\n"
|
||||||
|
"|.+... o S |\n"
|
||||||
|
"|E+ ++. . |\n"
|
||||||
|
"|B*++.. . |\n"
|
||||||
|
"|+o*o o . |\n"
|
||||||
|
"|+o*Bo . |\n"
|
||||||
|
"+-----------------+");
|
||||||
|
ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(1, 32)),
|
||||||
|
"+-----------------+\n"
|
||||||
|
"| .oo+ . .B=. |\n"
|
||||||
|
"| .o.+ . o o.= |\n"
|
||||||
|
"|o..+.. o . E * |\n"
|
||||||
|
"|oo..+ . * * |\n"
|
||||||
|
"|B o.....S. o . |\n"
|
||||||
|
"|+=o..... |\n"
|
||||||
|
"| + + . |\n"
|
||||||
|
"|o. .. |\n"
|
||||||
|
"|..o.. |\n"
|
||||||
|
"+-----------------+");
|
||||||
|
ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(2, 32)),
|
||||||
|
"+-----------------+\n"
|
||||||
|
"| ... .o.o.|\n"
|
||||||
|
"| o .=.E|\n"
|
||||||
|
"| . + o ...+.|\n"
|
||||||
|
"| * o = o ... |\n"
|
||||||
|
"| * + S & . |\n"
|
||||||
|
"| = + % @ |\n"
|
||||||
|
"| . . = X o |\n"
|
||||||
|
"| . . O . |\n"
|
||||||
|
"| . + |\n"
|
||||||
|
"+-----------------+");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace barrier
|
Loading…
Reference in New Issue