Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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',
)
1 change: 1 addition & 0 deletions src/api/iptux-core/CoreThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class CoreThread {
void RecvFile(FileInfo* file);
void RecvFileAsync(FileInfo* file);
enum CoreThreadErr getLastErr() const;
bool supportEncryption() const;

public:
sigc::signal<void(std::shared_ptr<const Event>)> signalEvent;
Expand Down
14 changes: 10 additions & 4 deletions src/api/iptux-core/ProgramData.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class ProgramData {
/** Sync ProgramData to ConfigFile */
void WriteProgData();

bool initPrivateKey();

const std::vector<NetSegment>& getNetSegments() const;
void setNetSegments(std::vector<NetSegment>&& netSegments);

Expand Down Expand Up @@ -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; }
Expand Down Expand Up @@ -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
Expand All @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#mesondefine SYSTEM_DARWIN
#mesondefine HAVE_APPINDICATOR
#mesondefine HAVE_ENCRYPTION

#if defined(__APPLE__) || defined(__CYGWIN__)
#define O_LARGEFILE 0
Expand Down
13 changes: 13 additions & 0 deletions src/iptux-core/CoreThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -360,6 +361,14 @@ CoreThread::CoreThread(shared_ptr<ProgramData> 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() {
Expand Down Expand Up @@ -1007,4 +1016,8 @@ enum CoreThreadErr CoreThread::getLastErr() const {
return pImpl->lastErr;
}

bool CoreThread::supportEncryption() const {
return pImpl->encrypt_msg;
}

} // namespace iptux
29 changes: 28 additions & 1 deletion src/iptux-core/ProgramData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Comment on lines +84 to +85
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 suggestion (security): Persisting the private key in plain text config may be a security concern

Persisting the private key in the general config store means any user/process with access to that file can read it. Depending on your threat model, consider a more secure storage mechanism (OS keyring/keystore, separate restricted file, or encrypting the key with a passphrase) rather than plain text in the config. At minimum, ensure the config file location is tightly permissioned if you keep this approach.

Suggested implementation:

  // Persist shared file list and public key in the general config store.
  config->SetStringList(CONFIG_SHARED_FILE_LIST, sharedFileList);
  config->SetString("public_key", public_key);

  // NOTE: We intentionally do NOT persist the private key in the general
  // config store to avoid storing it in plaintext in a world-readable file.
  // The private key should be loaded from a more secure storage mechanism
  // (OS keyring/keystore, separate file with restrictive permissions, or
  // encrypted on disk) instead of being written here.
  config->Save();
}

To fully address the concern, you should:

  1. Introduce a secure storage mechanism for private_key (e.g. OS keyring/keystore integration, or a separate file created with restrictive permissions such as 0600).
  2. Update ProgramData::initPrivateKey() to load the private key from that secure storage, and ensure it never falls back to reading it from the general config.
  3. Audit any other call sites that might read/write private_key via config and migrate them to the secure storage path instead.
  4. Ensure that any new secure-storage file is documented and added to ignore lists/backups as appropriate so it is not accidentally exposed.

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<NetSegment>& ProgramData::getNetSegments() const {
return netseg;
}
Expand Down Expand Up @@ -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());

Expand Down
20 changes: 14 additions & 6 deletions src/iptux-core/internal/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions src/iptux-core/internal/Encrypt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <string>

namespace iptux {

class Encrypt {
public:
static std::string genRsaPrivPem(int bits);
};

} // namespace iptux
52 changes: 52 additions & 0 deletions src/iptux-core/internal/EncryptGnutls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Encrypt.h"
#include "iptux-utils/output.h"
#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

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
29 changes: 29 additions & 0 deletions src/iptux-core/internal/EncryptTest.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
13 changes: 10 additions & 3 deletions src/iptux-core/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,27 @@ 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')

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,
Expand All @@ -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
Expand All @@ -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',
Expand Down
13 changes: 6 additions & 7 deletions src/iptux-utils/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,17 @@

#include <glib.h>

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
Expand Down
7 changes: 7 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading