diff --git a/meson_options.txt b/meson_options.txt index 3533d0ca6..caf6e80a2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -18,3 +18,24 @@ option( value: 'auto', description: 'enable app indicator support', ) + +option( + 'sanitize-address', + type: 'boolean', + value: false, + description: 'enable -fsanitize=address compile/link flag', +) + +option( + 'sanitize-undefined', + type: 'boolean', + value: false, + description: 'enable -fsanitize=undefined compile/link flag', +) + +option( + 'encryption', + type: 'feature', + value: 'auto', + description: 'enable message encryption support', +) diff --git a/src/api/iptux-core/CoreThread.h b/src/api/iptux-core/CoreThread.h index 886096a2c..7625e456d 100644 --- a/src/api/iptux-core/CoreThread.h +++ b/src/api/iptux-core/CoreThread.h @@ -182,6 +182,7 @@ class CoreThread { void RecvFile(FileInfo* file); void RecvFileAsync(FileInfo* file); enum CoreThreadErr getLastErr() const; + bool supportEncryption() const; public: sigc::signal)> signalEvent; diff --git a/src/api/iptux-core/ProgramData.h b/src/api/iptux-core/ProgramData.h index d5e3f5dd2..0cb7ad60f 100644 --- a/src/api/iptux-core/ProgramData.h +++ b/src/api/iptux-core/ProgramData.h @@ -23,6 +23,8 @@ class ProgramData { /** Sync ProgramData to ConfigFile */ void WriteProgData(); + bool initPrivateKey(); + const std::vector& getNetSegments() const; void setNetSegments(std::vector&& netSegments); @@ -53,6 +55,7 @@ class ProgramData { bool IsUsingBlacklist() const; bool IsFilterFileShareRequest() const; bool isHideTaskbarWhenMainWindowIconified() const; + bool isEncryptMsg() const { return encrypt_msg; } void set_port(uint16_t port, bool is_init = false); void setOpenChat(bool value) { open_chat = value; } void setHideStartup(bool value) { hide_startup = value; } @@ -82,10 +85,12 @@ class ProgramData { std::string path; // 存档路径 * std::string sign; // 个性签名 * - std::string codeset; // 候选编码 * - std::string encode; // 默认通信编码 * - char* palicon; // 默认头像 * - char* font; // 面板字体 * + std::string codeset; // 候选编码 * + std::string encode; // 默认通信编码 * + std::string public_key; // Public key for encryption * + std::string private_key; // Private key for encryption * + char* palicon; // 默认头像 * + char* font; // 面板字体 * struct timeval timestamp; // 程序数据时间戳 int send_message_retry_in_us; // sleep time(in microsecond) when send message @@ -108,6 +113,7 @@ class ProgramData { uint8_t proof_shared : 1; uint8_t hide_taskbar_when_main_window_iconified_ : 1; uint8_t need_restart_ : 1; + uint8_t encrypt_msg : 1; private: void InitSublayer(); diff --git a/src/config.h.in b/src/config.h.in index 446ee5399..918b54ffd 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -21,6 +21,7 @@ #mesondefine SYSTEM_DARWIN #mesondefine HAVE_APPINDICATOR +#mesondefine HAVE_ENCRYPTION #if defined(__APPLE__) || defined(__CYGWIN__) #define O_LARGEFILE 0 diff --git a/src/iptux-core/CoreThread.cpp b/src/iptux-core/CoreThread.cpp index e751a1468..23dd07ca9 100644 --- a/src/iptux-core/CoreThread.cpp +++ b/src/iptux-core/CoreThread.cpp @@ -310,6 +310,7 @@ struct CoreThread::Impl { PPalInfo me; UdpDataService_U udp_data_service; + uint8_t encrypt_msg : 1; GSList* blacklist{nullptr}; // 黑名单链表 bool debugDontBroadcast{false}; @@ -360,6 +361,14 @@ CoreThread::CoreThread(shared_ptr data) .setGroup(programData->mygroup) .setEncode("utf-8") .setCompatible(true); + + if (programData->isEncryptMsg()) { + if (!programData->initPrivateKey()) { + LOG_ERROR("CoreThread: Failed to initialize encryption keys."); + } else { + pImpl->encrypt_msg = true; + } + } } CoreThread::~CoreThread() { @@ -1007,4 +1016,8 @@ enum CoreThreadErr CoreThread::getLastErr() const { return pImpl->lastErr; } +bool CoreThread::supportEncryption() const { + return pImpl->encrypt_msg; +} + } // namespace iptux diff --git a/src/iptux-core/ProgramData.cpp b/src/iptux-core/ProgramData.cpp index 54d43137f..288e8d46d 100644 --- a/src/iptux-core/ProgramData.cpp +++ b/src/iptux-core/ProgramData.cpp @@ -6,7 +6,10 @@ #include "iptux-core/internal/ipmsg.h" #include "iptux-utils/output.h" -#include "iptux-utils/utils.h" + +#if HAVE_ENCRYPTION +#include "iptux-core/internal/Encrypt.h" +#endif using namespace std; @@ -78,9 +81,30 @@ void ProgramData::WriteProgData() { sharedFileList.push_back(fileInfo.filepath); } config->SetStringList(CONFIG_SHARED_FILE_LIST, sharedFileList); + config->SetString("public_key", public_key); + config->SetString("private_key", private_key); + config->SetBool("encrypt_msg", encrypt_msg); config->Save(); } +bool ProgramData::initPrivateKey() { + bool res; + + if (!private_key.empty()) { + return true; + } + +#if HAVE_ENCRYPTION + private_key = Encrypt::genRsaPrivPem(4096); + res = !private_key.empty(); + if (res) + this->WriteProgData(); + return res; +#else + return false; +#endif +} + const std::vector& ProgramData::getNetSegments() const { return netseg; } @@ -131,6 +155,9 @@ void ProgramData::ReadProgData() { set_port(config->GetInt("port", IPTUX_DEFAULT_PORT), true); codeset = config->GetString("candidacy_encode", "gb18030,utf-16"); encode = config->GetString("preference_encode", "utf-8"); + encrypt_msg = config->GetBool("encrypt_msg", true); + public_key = config->GetString("public_key"); + private_key = config->GetString("private_key"); palicon = g_strdup(config->GetString("pal_icon", "icon-qq.png").c_str()); font = g_strdup(config->GetString("panel_font", "Sans Serif 10").c_str()); diff --git a/src/iptux-core/internal/Command.cpp b/src/iptux-core/internal/Command.cpp index a4fd25c08..916063419 100644 --- a/src/iptux-core/internal/Command.cpp +++ b/src/iptux-core/internal/Command.cpp @@ -97,14 +97,24 @@ Command::Command(CoreThread& coreThread) */ Command::~Command() {} +static uint32_t getEntryCmd(const CoreThread& ct, bool dialup) { + uint32_t cmd = IPMSG_ABSENCEOPT | IPMSG_BR_ENTRY; + if (dialup) { + cmd |= IPMSG_DIALUPOPT; + } + if (ct.supportEncryption()) { + cmd |= IPMSG_ENCRYPTOPT; + } + return cmd; +} + /** * 向局域网所有计算机广播上线信息. * @param sock udp socket */ void Command::BroadCast(int sock, uint16_t port) { auto programData = coreThread.getProgramData(); - CreateCommand(IPMSG_ABSENCEOPT | IPMSG_BR_ENTRY, - programData->nickname.c_str()); + CreateCommand(getEntryCmd(coreThread, false), programData->nickname.c_str()); ConvertEncode(programData->encode); CreateIptuxExtra(programData->encode); @@ -122,8 +132,7 @@ void Command::BroadCast(int sock, uint16_t port) { */ void Command::DialUp(int sock, uint16_t port) { auto programData = coreThread.getProgramData(); - CreateCommand(IPMSG_DIALUPOPT | IPMSG_ABSENCEOPT | IPMSG_BR_ENTRY, - programData->nickname.c_str()); + CreateCommand(getEntryCmd(coreThread, true), programData->nickname.c_str()); ConvertEncode(programData->encode); CreateIptuxExtra(programData->encode); @@ -188,8 +197,7 @@ void Command::SendAbsence(int sock, CPPalInfo pal) { */ void Command::SendDetectPacket(int sock, in_addr ipv4, uint16_t port) { auto programData = coreThread.getProgramData(); - CreateCommand(IPMSG_DIALUPOPT | IPMSG_ABSENCEOPT | IPMSG_BR_ENTRY, - programData->nickname.c_str()); + CreateCommand(getEntryCmd(coreThread, true), programData->nickname.c_str()); ConvertEncode(programData->encode); CreateIptuxExtra(programData->encode); commandSendTo(sock, buf, size, 0, ipv4, port); diff --git a/src/iptux-core/internal/Encrypt.h b/src/iptux-core/internal/Encrypt.h new file mode 100644 index 000000000..6f26beac9 --- /dev/null +++ b/src/iptux-core/internal/Encrypt.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include + +namespace iptux { + +class Encrypt { + public: + static std::string genRsaPrivPem(int bits); +}; + +} // namespace iptux diff --git a/src/iptux-core/internal/EncryptGnutls.cpp b/src/iptux-core/internal/EncryptGnutls.cpp new file mode 100644 index 000000000..62285eb83 --- /dev/null +++ b/src/iptux-core/internal/EncryptGnutls.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "Encrypt.h" +#include "iptux-utils/output.h" +#include +#include +#include + +using namespace std; + +namespace iptux { + +std::string Encrypt::genRsaPrivPem(int bits) { + int ret; + gnutls_datum_t out = {NULL, 0}; + + if (bits < 512) { + LOG_WARN("Invalid RSA key bits: %d", bits); + return ""; + } + + gnutls_global_init(); + + gnutls_x509_privkey_t privkey; + ret = gnutls_x509_privkey_init(&privkey); + if (ret < 0) { + LOG_WARN("gnutls_x509_privkey_init failed: %s", gnutls_strerror(ret)); + return ""; + } + + ret = gnutls_x509_privkey_generate(privkey, GNUTLS_PK_RSA, bits, 0); + if (ret < 0) { + LOG_WARN("gnutls_x509_privkey_generate failed: %s", gnutls_strerror(ret)); + gnutls_x509_privkey_deinit(privkey); + return ""; + } + + ret = gnutls_x509_privkey_export2_pkcs8(privkey, GNUTLS_X509_FMT_PEM, NULL, 0, + &out); + if (ret < 0) { + LOG_WARN("gnutls_x509_privkey_export2_pkcs8 failed: %s", + gnutls_strerror(ret)); + gnutls_x509_privkey_deinit(privkey); + return ""; + } + + std::string keypair((char*)out.data, out.size); + gnutls_free(out.data); + gnutls_x509_privkey_deinit(privkey); + return keypair; +} + +} // namespace iptux diff --git a/src/iptux-core/internal/EncryptTest.cpp b/src/iptux-core/internal/EncryptTest.cpp new file mode 100644 index 000000000..219112c4c --- /dev/null +++ b/src/iptux-core/internal/EncryptTest.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Encrypt.h" +#include "gtest/gtest.h" + +struct GenRsaPrivPemTest { + int bits; + bool success; + bool fail; +}; + +TEST(Encrypt, GenRsaPrivPem) { + struct GenRsaPrivPemTest tests[] = { + {1024, .success = 1}, {2048, .success = 1}, {4096, .success = 1}, + {512, .success = 1}, {0, .fail = 1}, {-1, .fail = 1}, + {1025, .success = 1}, {1032, .success = 1}, + }; + + for (const auto& test : tests) { + std::string priv_pem = iptux::Encrypt::genRsaPrivPem(test.bits); + if (test.success) { + ASSERT_FALSE(priv_pem.empty()) << "bits=" << test.bits; + } + + if (test.fail) { + ASSERT_TRUE(priv_pem.empty()) << "bits=" << test.bits; + } + } +} \ No newline at end of file diff --git a/src/iptux-core/meson.build b/src/iptux-core/meson.build index 785b8651f..2f438e062 100644 --- a/src/iptux-core/meson.build +++ b/src/iptux-core/meson.build @@ -27,6 +27,12 @@ core_sources += files([ 'internal/UdpDataService.cpp', ]) +if encryption_dep.found() + core_sources += files([ + 'internal/EncryptGnutls.cpp', + ]) +endif + inc = include_directories('..', '../api') thread_dep = dependency('threads') @@ -34,14 +40,14 @@ thread_dep = dependency('threads') if get_option('static-link') libiptux_core = static_library('iptux-core', core_sources, - dependencies: [glib_dep, jsoncpp_dep, sigc_dep, thread_dep], + dependencies: [glib_dep, jsoncpp_dep, sigc_dep, thread_dep, encryption_dep], link_with: [libiptux_utils], include_directories: inc, ) else libiptux_core = shared_library('iptux-core', core_sources, - dependencies: [glib_dep, jsoncpp_dep, sigc_dep, thread_dep], + dependencies: [glib_dep, jsoncpp_dep, sigc_dep, thread_dep, encryption_dep], link_with: [libiptux_utils], include_directories: inc, install: true, @@ -53,7 +59,7 @@ else description: 'Communicate and Share File in LAN', name: 'iptux-core', version: meson.project_version(), - requires: [jsoncpp_dep, glib_dep, sigc_dep, thread_dep], + requires: [jsoncpp_dep, glib_dep, sigc_dep, thread_dep, encryption_dep], libraries: [libiptux_core], ) endif @@ -77,6 +83,7 @@ core_test_sources = files([ 'internal/supportTest.cpp', 'internal/UdpDataTest.cpp', 'internal/UdpDataServiceTest.cpp', + 'internal/EncryptTest.cpp', 'IptuxConfigTest.cpp', 'ModelsTest.cpp', 'ProgramDataTest.cpp', diff --git a/src/iptux-utils/output.h b/src/iptux-utils/output.h index bd8d33acf..e2b280ce8 100644 --- a/src/iptux-utils/output.h +++ b/src/iptux-utils/output.h @@ -14,18 +14,17 @@ #include -namespace iptux { - #define LOG_DEBUG(...) \ - DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_DEBUG, __VA_ARGS__) + iptux::DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_DEBUG, __VA_ARGS__) #define LOG_INFO(...) \ - DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_INFO, __VA_ARGS__) + iptux::DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_INFO, __VA_ARGS__) #define LOG_WARN(...) \ - DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_WARNING, __VA_ARGS__) + iptux::DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_WARNING, __VA_ARGS__) #define LOG_CRIT(...) \ - DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_CRITICAL, __VA_ARGS__) + iptux::DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_CRITICAL, __VA_ARGS__) #define LOG_ERROR(...) \ - DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_ERROR, __VA_ARGS__) + iptux::DoLog(__FILE__, __LINE__, __func__, G_LOG_LEVEL_ERROR, __VA_ARGS__) +namespace iptux { /* 警告信息输出 */ #ifndef WARNING diff --git a/src/meson.build b/src/meson.build index 5a2cc76b8..2f8436d5e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -39,6 +39,13 @@ else conf_data.set('HAVE_APPINDICATOR', 0) endif +encryption_dep = dependency('gnutls', required: get_option('encryption')) +if encryption_dep.found() + conf_data.set('HAVE_ENCRYPTION', 1) +else + conf_data.set('HAVE_ENCRYPTION', 0) +endif + configure_file( input: 'config.h.in', output: 'config.h',