lib/net: Implement a reusable fingerprint database

This commit is contained in:
Povilas Kanapickas 2021-11-01 02:52:44 +02:00
parent 3e71b468f6
commit 9cac96b4af
5 changed files with 351 additions and 5 deletions

View File

@ -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 <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;
}
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

View File

@ -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_NET_FINGERPRINT_DATABASE_H
#define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H
#include "FingerprintType.h"
#include <iosfwd>
#include <string>
#include <vector>
namespace barrier {
struct FingerprintData {
std::string algorithm;
std::vector<std::uint8_t> 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<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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef BARRIER_LIB_NET_FINGERPRINT_TYPE_H
#define BARRIER_LIB_NET_FINGERPRINT_TYPE_H
#include <string>
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

View File

@ -18,6 +18,7 @@
#ifndef BARRIER_LIB_NET_SECUREUTILS_H
#define BARRIER_LIB_NET_SECUREUTILS_H
#include "FingerprintType.h"
#include <openssl/ossl_typ.h>
#include <cstdint>
#include <string>
@ -25,11 +26,6 @@
namespace barrier {
enum FingerprintType {
SHA1, // deprecated
SHA256,
};
std::string format_ssl_fingerprint(const std::vector<std::uint8_t>& fingerprint,
bool separator = true);

View File

@ -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