diff --git a/src/lib/net/FingerprintDatabase.cpp b/src/lib/net/FingerprintDatabase.cpp
new file mode 100644
index 00000000..cdc3ad32
--- /dev/null
+++ b/src/lib/net/FingerprintDatabase.cpp
@@ -0,0 +1,140 @@
+/*
+ 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 .
+*/
+
+#include "base/String.h"
+#include "FingerprintDatabase.h"
+#include "io/fstream.h"
+#include
+#include
+
+namespace barrier {
+
+bool FingerprintData::operator==(const FingerprintData& other) const
+{
+ return algorithm == other.algorithm && data == other.data;
+}
+
+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
diff --git a/src/lib/net/FingerprintDatabase.h b/src/lib/net/FingerprintDatabase.h
new file mode 100644
index 00000000..36ab39ce
--- /dev/null
+++ b/src/lib/net/FingerprintDatabase.h
@@ -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 .
+*/
+
+#ifndef BARRIER_LIB_NET_FINGERPRINT_DATABASE_H
+#define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H
+
+#include "FingerprintType.h"
+#include
+#include
+#include
+
+namespace barrier {
+
+struct FingerprintData {
+ std::string algorithm;
+ std::vector data;
+
+ bool valid() const { return !algorithm.empty(); }
+
+ bool operator==(const FingerprintData& other) const;
+};
+
+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& 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 fingerprints_;
+};
+
+} // namespace barrier
+
+#endif // BARRIER_LIB_NET_FINGERPRINT_DATABASE_H
diff --git a/src/lib/net/FingerprintType.h b/src/lib/net/FingerprintType.h
new file mode 100644
index 00000000..4e58e9f6
--- /dev/null
+++ b/src/lib/net/FingerprintType.h
@@ -0,0 +1,54 @@
+/*
+ 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 .
+*/
+
+#ifndef BARRIER_LIB_NET_FINGERPRINT_TYPE_H
+#define BARRIER_LIB_NET_FINGERPRINT_TYPE_H
+
+#include
+
+namespace barrier {
+
+enum FingerprintType {
+ INVALID,
+ SHA1, // deprecated
+ SHA256,
+};
+
+inline 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";
+}
+
+inline 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
+
+#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H
diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h
index df450136..ace0d45a 100644
--- a/src/lib/net/SecureUtils.h
+++ b/src/lib/net/SecureUtils.h
@@ -18,6 +18,7 @@
#ifndef BARRIER_LIB_NET_SECUREUTILS_H
#define BARRIER_LIB_NET_SECUREUTILS_H
+#include "FingerprintType.h"
#include
#include
#include
@@ -25,11 +26,6 @@
namespace barrier {
-enum FingerprintType {
- SHA1, // deprecated
- SHA256,
-};
-
std::string format_ssl_fingerprint(const std::vector& fingerprint,
bool separator = true);
diff --git a/src/test/unittests/net/FingerprintDatabaseTests.cpp b/src/test/unittests/net/FingerprintDatabaseTests.cpp
new file mode 100644
index 00000000..61bed0ea
--- /dev/null
+++ b/src/test/unittests/net/FingerprintDatabaseTests.cpp
@@ -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 .
+*/
+
+#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 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