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 (sigwait HAVE_POSIX_SIGWAIT)
|
||||
check_function_exists (strftime HAVE_STRFTIME)
|
||||
check_function_exists (vsnprintf HAVE_VSNPRINTF)
|
||||
check_function_exists (inet_aton HAVE_INET_ATON)
|
||||
|
||||
# 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. */
|
||||
#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. */
|
||||
#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/DataDownloader.cpp
|
||||
src/DisplayIsValid.cpp
|
||||
src/Fingerprint.cpp
|
||||
src/HotkeyDialog.cpp
|
||||
src/IpcClient.cpp
|
||||
src/Ipc.cpp
|
||||
|
@ -70,7 +69,6 @@ set(GUI_HEADER_FILES
|
|||
src/DataDownloader.h
|
||||
src/DisplayIsValid.h
|
||||
src/ElevateMode.h
|
||||
src/Fingerprint.h
|
||||
src/HotkeyDialog.h
|
||||
src/IpcClient.h
|
||||
src/Ipc.h
|
||||
|
@ -131,7 +129,7 @@ add_executable (barrier WIN32
|
|||
|
||||
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_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 "Fingerprint.h"
|
||||
#include "AboutDialog.h"
|
||||
#include "ServerConfigDialog.h"
|
||||
#include "SettingsDialog.h"
|
||||
|
@ -31,7 +30,10 @@
|
|||
#include "ProcessorArch.h"
|
||||
#include "SslCertificate.h"
|
||||
#include "ShutdownCh.h"
|
||||
#include "base/String.h"
|
||||
#include "common/DataDirectories.h"
|
||||
#include "net/FingerprintDatabase.h"
|
||||
#include "net/SecureUtils.h"
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
|
@ -156,9 +158,22 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
|
|||
|
||||
m_pComboServerList->hide();
|
||||
m_pLabelPadlock->hide();
|
||||
frame_fingerprint_details->hide();
|
||||
|
||||
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(0, 0);
|
||||
}
|
||||
|
@ -412,13 +427,29 @@ void MainWindow::checkConnected(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)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString fingerprint = fingerprintRegex.cap(1);
|
||||
if (Fingerprint::trustedServers().isTrusted(fingerprint)) {
|
||||
barrier::FingerprintData fingerprint_sha1 = {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -432,7 +463,11 @@ void MainWindow::checkFingerprint(const QString& line)
|
|||
QMessageBox::information(
|
||||
this, tr("Security question"),
|
||||
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 "
|
||||
"fingerprint to the one on your server's screen. If the "
|
||||
"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 "
|
||||
"connections, click Yes. To reject this fingerprint and "
|
||||
"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);
|
||||
|
||||
if (fingerprintReply == QMessageBox::Yes) {
|
||||
// restart core process after trusting fingerprint.
|
||||
Fingerprint::trustedServers().trust(fingerprint);
|
||||
db.add_trusted(fingerprint_sha256);
|
||||
db.write(db_path);
|
||||
startBarrier();
|
||||
}
|
||||
|
||||
|
@ -925,6 +964,14 @@ void MainWindow::changeEvent(QEvent* event)
|
|||
QMainWindow::changeEvent(event);
|
||||
}
|
||||
|
||||
bool MainWindow::event(QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::LayoutRequest) {
|
||||
setFixedSize(sizeHint());
|
||||
}
|
||||
return QMainWindow::event(event);
|
||||
}
|
||||
|
||||
void MainWindow::updateZeroconfService()
|
||||
{
|
||||
QMutexLocker locker(&m_UpdateZeroconfMutex);
|
||||
|
@ -965,12 +1012,47 @@ void MainWindow::updateSSLFingerprint()
|
|||
});
|
||||
m_pSslCertificate->generateCertificate();
|
||||
}
|
||||
if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) {
|
||||
m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst());
|
||||
m_pLabelLocalFingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
} else {
|
||||
|
||||
toolbutton_show_fingerprint->setEnabled(false);
|
||||
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)
|
||||
|
|
|
@ -157,6 +157,7 @@ public slots:
|
|||
void stopService();
|
||||
void stopDesktop();
|
||||
void changeEvent(QEvent* event);
|
||||
bool event(QEvent* event);
|
||||
void retranslateMenuBar();
|
||||
#if defined(Q_OS_WIN)
|
||||
bool isServiceRunning(QString name);
|
||||
|
@ -202,6 +203,8 @@ public slots:
|
|||
QStringList m_PendingClientNames;
|
||||
LogWindow *m_pLogWindow;
|
||||
|
||||
bool m_fingerprint_expanded = false;
|
||||
|
||||
private slots:
|
||||
void on_m_pCheckBoxAutoConfig_toggled(bool checked);
|
||||
void on_m_pComboServerList_currentIndexChanged(QString );
|
||||
|
|
|
@ -2,31 +2,20 @@
|
|||
<ui version="4.0">
|
||||
<class>MainWindowBase</class>
|
||||
<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">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Barrier</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="m_pGroupServer">
|
||||
<property name="sizePolicy">
|
||||
|
@ -86,10 +75,87 @@
|
|||
<property name="text">
|
||||
<string/>
|
||||
</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>
|
||||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
<widget class="QRadioButton" name="m_pRadioInternalConfig">
|
||||
<property name="text">
|
||||
|
@ -253,7 +319,7 @@
|
|||
<string/>
|
||||
</property>
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -399,7 +465,7 @@
|
|||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="Barrier.qrc"/>
|
||||
<include location="../res/Barrier.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
|
|
|
@ -16,8 +16,11 @@
|
|||
*/
|
||||
|
||||
#include "SslCertificate.h"
|
||||
#include "Fingerprint.h"
|
||||
#include "common/DataDirectories.h"
|
||||
#include "base/finally.h"
|
||||
#include "io/fstream.h"
|
||||
#include "net/FingerprintDatabase.h"
|
||||
#include "net/SecureUtils.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QDir>
|
||||
|
@ -29,16 +32,8 @@
|
|||
#include <openssl/pem.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 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) :
|
||||
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()
|
||||
{
|
||||
auto filename = QString::fromStdString(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");
|
||||
auto cert_path = getCertificatePath();
|
||||
|
||||
QFile file(QString::fromStdString(cert_path));
|
||||
if (!file.exists() || !isCertificateValid(cert_path)) {
|
||||
QDir sslDir(QString::fromStdString(getCertificateDirectory()));
|
||||
if (!sslDir.exists()) {
|
||||
sslDir.mkpath(".");
|
||||
}
|
||||
|
||||
// key output filename
|
||||
arguments.append("-keyout");
|
||||
arguments.append(filename);
|
||||
|
||||
// certificate output filename
|
||||
arguments.append("-out");
|
||||
arguments.append(filename);
|
||||
|
||||
if (!runTool(arguments).first) {
|
||||
try {
|
||||
barrier::generate_pem_self_signed_cert(cert_path);
|
||||
} catch (const std::exception& e) {
|
||||
emit error(QString("SSL tool failed: %1").arg(e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
emit info(tr("SSL certificate generated."));
|
||||
}
|
||||
|
||||
generateFingerprint(filename);
|
||||
generateFingerprint(cert_path);
|
||||
|
||||
emit generateFinished();
|
||||
}
|
||||
|
||||
void SslCertificate::generateFingerprint(const QString& certificateFilename)
|
||||
void SslCertificate::generateFingerprint(const std::string& cert_path)
|
||||
{
|
||||
QStringList arguments;
|
||||
arguments.append("x509");
|
||||
arguments.append("-fingerprint");
|
||||
arguments.append("-sha1");
|
||||
arguments.append("-noout");
|
||||
arguments.append("-in");
|
||||
arguments.append(certificateFilename);
|
||||
try {
|
||||
auto local_path = DataDirectories::local_ssl_fingerprints_path();
|
||||
barrier::FingerprintDatabase db;
|
||||
db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path,
|
||||
barrier::FingerprintType::SHA1));
|
||||
db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path,
|
||||
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."));
|
||||
}
|
||||
else {
|
||||
emit error(tr("Failed to find SSL fingerprint."));
|
||||
} catch (const std::exception& e) {
|
||||
emit error(tr("Failed to find SSL fingerprint.") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,42 +97,35 @@ std::string SslCertificate::getCertificateDirectory()
|
|||
return m_ProfileDir + QDir::separator().toLatin1() + kSslDir;
|
||||
}
|
||||
|
||||
bool SslCertificate::isCertificateValid(const QString& path)
|
||||
bool SslCertificate::isCertificateValid(const std::string& path)
|
||||
{
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_load_BIO_strings();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
BIO* bio = BIO_new(BIO_s_file());
|
||||
|
||||
auto ret = BIO_read_filename(bio, path.toStdString().c_str());
|
||||
if (!ret) {
|
||||
auto fp = barrier::fopen_utf8_path(path, "r");
|
||||
if (!fp) {
|
||||
emit info(tr("Could not read from default certificate file."));
|
||||
BIO_free_all(bio);
|
||||
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) {
|
||||
emit info(tr("Error loading default certificate file to memory."));
|
||||
BIO_free_all(bio);
|
||||
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) {
|
||||
emit info(tr("Default certificate key file does not contain valid public key"));
|
||||
X509_free(cert);
|
||||
BIO_free_all(bio);
|
||||
return false;
|
||||
}
|
||||
auto pubkey_free = barrier::finally([pubkey]() { EVP_PKEY_free(pubkey); });
|
||||
|
||||
auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey));
|
||||
if (type != EVP_PKEY_RSA && type != EVP_PKEY_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;
|
||||
}
|
||||
|
||||
|
@ -233,14 +133,8 @@ bool SslCertificate::isCertificateValid(const QString& path)
|
|||
if (bits < 2048) {
|
||||
// We could have small keys in old barrier installations
|
||||
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;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pubkey);
|
||||
X509_free(cert);
|
||||
BIO_free_all(bio);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -37,12 +37,12 @@ signals:
|
|||
|
||||
private:
|
||||
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 getCertificateDirectory();
|
||||
|
||||
bool isCertificateValid(const QString& path);
|
||||
bool isCertificateValid(const std::string& path);
|
||||
private:
|
||||
std::string m_ProfileDir;
|
||||
};
|
||||
|
|
|
@ -46,16 +46,6 @@ public:
|
|||
//! @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
|
||||
virtual int convStringMBToWC(wchar_t*,
|
||||
const char*, UInt32 n, bool* errors);
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
//
|
||||
|
||||
#include "arch/multibyte.h"
|
||||
#include "arch/vsnprintf.h"
|
||||
|
||||
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
|
||||
//
|
||||
|
||||
#include "arch/multibyte.h"
|
||||
#define HAVE_VSNPRINTF 1
|
||||
#define ARCH_VSNPRINTF _vsnprintf
|
||||
#include "arch/vsnprintf.h"
|
||||
|
||||
ArchStringWindows::ArchStringWindows()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ Log::print(const char* file, int line, const char* fmt, ...)
|
|||
// try printing into the buffer
|
||||
va_list args;
|
||||
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);
|
||||
|
||||
// if the buffer wasn't big enough then make it bigger and try again
|
||||
|
|
|
@ -35,6 +35,42 @@
|
|||
namespace barrier {
|
||||
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
|
||||
format(const char* fmt, ...)
|
||||
{
|
||||
|
@ -135,7 +171,7 @@ sprintf(const char* fmt, ...)
|
|||
// try printing into the buffer
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int n = ARCH->vsnprintf(buffer, len, fmt, args);
|
||||
int n = std::vsnprintf(buffer, len, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void
|
||||
toHex(std::string& subject, int width, const char fill)
|
||||
std::string to_hex(const std::vector<std::uint8_t>& subject, int width, const char fill)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex;
|
||||
for (unsigned int i = 0; i < subject.length(); i++) {
|
||||
ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i];
|
||||
for (unsigned int i = 0; i < subject.size(); 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
|
||||
|
|
|
@ -75,7 +75,10 @@ std::string removeFileExt(std::string filename);
|
|||
/*!
|
||||
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
|
||||
/*!
|
||||
|
|
|
@ -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(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:
|
||||
// static class
|
||||
DataDirectories() {}
|
||||
|
||||
static std::string _profile;
|
||||
static std::string _global;
|
||||
static std::string _systemconfig;
|
||||
|
|
|
@ -21,3 +21,28 @@
|
|||
std::string DataDirectories::_profile;
|
||||
std::string DataDirectories::_global;
|
||||
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:
|
||||
static std::string basename(const std::string& path);
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#ifndef BARRIER_LIB_IO_FSTREAM_H
|
||||
#define BARRIER_LIB_IO_FSTREAM_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <iosfwd>
|
||||
#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,
|
||||
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
|
||||
|
||||
#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 "SecureUtils.h"
|
||||
|
||||
#include "net/TSocketMultiplexerMethodJob.h"
|
||||
#include "base/TMethodEventJob.h"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include "base/String.h"
|
||||
#include "common/DataDirectories.h"
|
||||
#include "io/fstream.h"
|
||||
#include "net/FingerprintDatabase.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
@ -47,11 +49,6 @@ enum {
|
|||
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 {
|
||||
SSL_CTX* m_context;
|
||||
SSL* m_ssl;
|
||||
|
@ -656,83 +653,50 @@ SecureSocket::disconnect()
|
|||
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
|
||||
SecureSocket::verifyCertFingerprint()
|
||||
{
|
||||
// calculate received certificate fingerprint
|
||||
X509 *cert = cert = SSL_get_peer_certificate(m_ssl->m_ssl);
|
||||
EVP_MD* tempDigest;
|
||||
unsigned char tempFingerprint[EVP_MAX_MD_SIZE];
|
||||
unsigned int tempFingerprintLen;
|
||||
tempDigest = (EVP_MD*)EVP_sha1();
|
||||
int digestResult = X509_digest(cert, tempDigest, tempFingerprint, &tempFingerprintLen);
|
||||
|
||||
if (digestResult <= 0) {
|
||||
LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult));
|
||||
barrier::FingerprintData fingerprint_sha1, fingerprint_sha256;
|
||||
try {
|
||||
auto* cert = SSL_get_peer_certificate(m_ssl->m_ssl);
|
||||
fingerprint_sha1 = barrier::get_ssl_cert_fingerprint(cert,
|
||||
barrier::FingerprintType::SHA1);
|
||||
fingerprint_sha256 = barrier::get_ssl_cert_fingerprint(cert,
|
||||
barrier::FingerprintType::SHA256);
|
||||
} catch (const std::exception& e) {
|
||||
LOG((CLOG_ERR "%s", e.what()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// format fingerprint into hexdecimal format with colon separator
|
||||
std::string fingerprint(reinterpret_cast<char*>(tempFingerprint), tempFingerprintLen);
|
||||
formatFingerprint(fingerprint);
|
||||
LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str()));
|
||||
// note: the GUI parses the following two lines of logs, don't change unnecessarily
|
||||
LOG((CLOG_NOTE "server fingerprint (SHA1): %s (SHA256): %s",
|
||||
barrier::format_ssl_fingerprint(fingerprint_sha1.data).c_str(),
|
||||
barrier::format_ssl_fingerprint(fingerprint_sha256.data).c_str()));
|
||||
|
||||
std::string trustedServersFilename;
|
||||
trustedServersFilename = barrier::string::sprintf(
|
||||
"%s/%s/%s",
|
||||
DataDirectories::profile().c_str(),
|
||||
kFingerprintDirName,
|
||||
kFingerprintTrustedServersFilename);
|
||||
auto fingerprint_db_path = DataDirectories::trusted_servers_ssl_fingerprints_path();
|
||||
|
||||
// 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
|
||||
std::string fileLine;
|
||||
std::ifstream file;
|
||||
barrier::open_utf8_path(file, trustedServersFilename);
|
||||
barrier::FingerprintDatabase db;
|
||||
db.read(fingerprint_db_path);
|
||||
|
||||
if (!file.is_open()) {
|
||||
LOG((CLOG_NOTE "Unable to open trustedServersFile: %s", trustedServersFilename.c_str() ));
|
||||
if (!db.fingerprints().empty()) {
|
||||
LOG((CLOG_NOTE "Read %d fingerprints from: %s", db.fingerprints().size(),
|
||||
fingerprint_db_path.c_str()));
|
||||
} 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;
|
||||
while (!file.eof() && file.is_open()) {
|
||||
getline(file,fileLine);
|
||||
if (!fileLine.empty()) {
|
||||
if (fileLine.compare(fingerprint) == 0) {
|
||||
if (db.is_trusted(fingerprint_sha256)) {
|
||||
LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint"));
|
||||
isValid = true;
|
||||
break;
|
||||
return true;
|
||||
} else {
|
||||
LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return isValid;
|
||||
}
|
||||
|
||||
MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job,
|
||||
|
|
|
@ -68,7 +68,6 @@ private:
|
|||
void showError(const std::string& reason);
|
||||
std::string getError();
|
||||
void disconnect();
|
||||
void formatFingerprint(std::string& fingerprint, bool hex = true, bool separator = true);
|
||||
bool verifyCertFingerprint();
|
||||
|
||||
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:
|
||||
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)
|
||||
{
|
||||
String subject = "foobar";
|
||||
std::vector<std::uint8_t> subject{'f', 'o', 'o', 'b', 'a', 'r'};
|
||||
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)
|
||||
|
|
|
@ -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