Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ option(
value: false,
description: 'enable -fsanitize=address compile/link flag',
)

option(
'encryption',
type: 'feature',
value: 'auto',
description: 'enable message encryption support',
)
12 changes: 8 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 @@ -82,10 +84,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 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
28 changes: 27 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->Save();
}

bool ProgramData::initPrivateKey() {
if (!private_key.empty()) {
return true;
}

#if HAVE_ENCRYPTION
auto keypair = generate_rsa_keypair(4096);
if (keypair.first.empty() || keypair.second.empty()) {
LOG_ERROR("Generate RSA keypair failed");
return false;
}
public_key = keypair.first;
private_key = keypair.second;
return true;
#else
return false;
#endif
}

const std::vector<NetSegment>& ProgramData::getNetSegments() const {
return netseg;
}
Expand Down Expand Up @@ -131,6 +155,8 @@ 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");
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
5 changes: 5 additions & 0 deletions src/iptux-core/internal/Encrypt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

#include <string>

std::pair<std::string, std::string> generate_rsa_keypair(int bits);
30 changes: 30 additions & 0 deletions src/iptux-core/internal/EncryptGnutls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "Encrypt.h"
#include "iptux-utils/output.h"
#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

using namespace std;

std::pair<std::string, std::string> generate_rsa_keypair(int bits) {
pair<string, string> keypair;
int ret;

gnutls_global_init();

gnutls_privkey_t privkey;
ret = gnutls_privkey_init(&privkey);
if (ret < 0) {
LOG_WARN("gnutls_privkey_init failed: %s", gnutls_strerror(ret));
return keypair;
}

ret = gnutls_privkey_generate(privkey, GNUTLS_PK_RSA, bits, 0);
Copy link

Choose a reason for hiding this comment

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

suggestion (bug_risk): Missing error handling for gnutls_global_init and potential repeated global init/deinit imbalance

gnutls_global_init()’s return value is ignored, so initialization failures may go unnoticed and lead to later misbehavior. Also, calling it on every keypair generation without matching gnutls_global_deinit() can cause an init/deinit imbalance. Please handle and log errors from gnutls_global_init(), and consider centralizing global init/deinit or otherwise ensuring they remain balanced.

Suggested implementation:

using namespace std;

namespace {

class GnutlsGlobalContext {
 public:
  GnutlsGlobalContext() : ok_(false) {
    int ret = gnutls_global_init();
    if (ret < 0) {
      LOG_WARN("gnutls_global_init failed: %s", gnutls_strerror(ret));
    } else {
      ok_ = true;
    }
  }

  ~GnutlsGlobalContext() {
    if (ok_) {
      gnutls_global_deinit();
    }
  }

  bool ok() const { return ok_; }

 private:
  bool ok_;
};

}  // namespace

std::pair<std::string, std::string> generate_rsa_keypair(int bits) {
  pair<string, string> keypair;
  int ret;

  static GnutlsGlobalContext gnutls_global_context;
  if (!gnutls_global_context.ok()) {
    // gnutls_global_init() has already been logged in GnutlsGlobalContext
    return keypair;
  }

  gnutls_privkey_t privkey;

if (ret < 0) {
LOG_WARN("gnutls_privkey_generate failed: %s", gnutls_strerror(ret));
gnutls_privkey_deinit(privkey);
return keypair;
}

return keypair;
}
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
9 changes: 8 additions & 1 deletion 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 All @@ -57,4 +64,4 @@ subdir('api')
subdir('iptux-utils')
subdir('iptux-core')
subdir('iptux')
subdir('main')
subdir('main')