Skip to content

Conversation

@lidaobing
Copy link
Member

@lidaobing lidaobing commented Jan 5, 2026

Core

  • generate RSA private key
  • send flag in ENTRY
  • honor GET_PUBKEY request
  • honor encrypt message
  • send GET_PUBKEY request
  • send encrypted message

GUI

  • add an option to disable encrypting messages
  • distinguish whether the peer support encrypting
  • distinguish whether the message is encrypted

Test

  • encrypt chat with IPMsg

@sourcery-ai
Copy link

sourcery-ai bot commented Jan 5, 2026

Reviewer's Guide

Adds optional encryption support backed by GnuTLS, including RSA keypair generation and storage in ProgramData, plus a small logging macro fix to ensure logging goes through the iptux namespace.

Sequence diagram for ProgramData private key initialization with encryption

sequenceDiagram
  participant ProgramData
  participant Encrypt as Encrypt_generate_rsa_keypair
  participant GnuTLS as GnuTLS_library

  ProgramData->>ProgramData: initPrivateKey()
  alt private_key not empty
    ProgramData-->>ProgramData: return true
  else private_key empty and HAVE_ENCRYPTION
    ProgramData->>Encrypt: generate_rsa_keypair(4096)
    Encrypt->>GnuTLS: gnutls_global_init()
    Encrypt->>GnuTLS: gnutls_privkey_init(privkey)
    alt init error
      GnuTLS-->>Encrypt: error code
      Encrypt-->>ProgramData: empty keypair
      ProgramData-->>ProgramData: LOG_ERROR Generate RSA keypair failed
      ProgramData-->>ProgramData: return false
    else init ok
      GnuTLS-->>Encrypt: success
      Encrypt->>GnuTLS: gnutls_privkey_generate(privkey, GNUTLS_PK_RSA, 4096, 0)
      alt generate error
        GnuTLS-->>Encrypt: error code
        Encrypt-->>ProgramData: empty keypair
        ProgramData-->>ProgramData: LOG_ERROR Generate RSA keypair failed
        ProgramData-->>ProgramData: return false
      else generate ok
        GnuTLS-->>Encrypt: success
        Encrypt-->>ProgramData: keypair
        ProgramData->>ProgramData: public_key = keypair.first
        ProgramData->>ProgramData: private_key = keypair.second
        ProgramData-->>ProgramData: return true
      end
    end
  else HAVE_ENCRYPTION disabled
    ProgramData-->>ProgramData: return false
  end
Loading

Class diagram for updated ProgramData and new Encrypt utility

classDiagram
  class ProgramData {
    +void WriteProgData()
    +bool initPrivateKey()
    +const std::vector~NetSegment~& getNetSegments() const
    +void setNetSegments(std::vector~NetSegment~&& netSegments)
    -std::string path
    -std::string sign
    -std::string codeset
    -std::string encode
    -std::string public_key
    -std::string private_key
    -char* palicon
    -char* font
    -timeval timestamp
    -int send_message_retry_in_us
  }

  class Encrypt {
    <<utility>>
    +std::pair~std::string, std::string~ generate_rsa_keypair(int bits)
  }

  ProgramData ..> Encrypt : uses
Loading

File-Level Changes

Change Details Files
Persist RSA public/private keys in ProgramData configuration and provide initialization helper.
  • Extend ProgramData with public_key and private_key fields for encryption
  • Read public_key and private_key from the config on startup
  • Write public_key and private_key back to the config when saving program data
  • Add initPrivateKey() to lazily generate a keypair if no private key exists and encryption is enabled
src/iptux-core/ProgramData.cpp
src/api/iptux-core/ProgramData.h
Introduce GnuTLS-based RSA keypair generation utility, guarded by build-time encryption support.
  • Add Encrypt.h declaring generate_rsa_keypair(int bits) returning public/private key strings
  • Implement generate_rsa_keypair in EncryptGnutls.cpp using GnuTLS RSA APIs, with logging on errors
  • Include Encrypt.h in ProgramData.cpp only when HAVE_ENCRYPTION is defined
src/iptux-core/internal/Encrypt.h
src/iptux-core/internal/EncryptGnutls.cpp
src/iptux-core/ProgramData.cpp
Wire up optional encryption feature at build time via Meson and config header.
  • Add a Meson feature option 'encryption' defaulting to 'auto'
  • Find and require GnuTLS dependency based on the encryption option
  • Set HAVE_ENCRYPTION in generated config.h based on whether GnuTLS is found
meson_options.txt
src/meson.build
src/config.h.in
Fix logging macros to use the iptux namespace-qualified DoLog symbol.
  • Update LOG_* macros to call iptux::DoLog instead of an unqualified DoLog
  • Move the iptux namespace declaration after the macro definitions to match the qualified usage
src/iptux-utils/output.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • generate_rsa_keypair currently initializes GnuTLS and generates a key but never exports the public/private key material or deinitializes the gnutls_privkey_t (and ignores gnutls_global_init errors), so you may want to add PEM export + cleanup to actually populate the returned pair and avoid resource leaks.
  • ProgramData::WriteProgData persists public_key and private_key directly into the config backend as plain strings; if this config is stored on disk, consider whether the private key should be encrypted, protected with permissions, or stored in a more secure location.
  • initPrivateKey is only compiled when HAVE_ENCRYPTION is set but the public_key/private_key fields and related config reads/writes are always present; if encryption is disabled at build time, you might want to avoid touching these fields or clearly separate the encryption-specific logic to reduce confusion about behavior in non-encryption builds.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- generate_rsa_keypair currently initializes GnuTLS and generates a key but never exports the public/private key material or deinitializes the gnutls_privkey_t (and ignores gnutls_global_init errors), so you may want to add PEM export + cleanup to actually populate the returned pair and avoid resource leaks.
- ProgramData::WriteProgData persists public_key and private_key directly into the config backend as plain strings; if this config is stored on disk, consider whether the private key should be encrypted, protected with permissions, or stored in a more secure location.
- initPrivateKey is only compiled when HAVE_ENCRYPTION is set but the public_key/private_key fields and related config reads/writes are always present; if encryption is disabled at build time, you might want to avoid touching these fields or clearly separate the encryption-specific logic to reduce confusion about behavior in non-encryption builds.

## Individual Comments

### Comment 1
<location> `src/iptux-core/internal/EncryptGnutls.cpp:9-18` </location>
<code_context>
+
+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;
+  }
+
</code_context>

<issue_to_address>
**issue (bug_risk):** RSA keypair is never exported into the returned strings and resources are not cleaned up

The function creates a `gnutls_privkey_t` but never exports the key material into the returned `std::string`s, so callers like `ProgramData::initPrivateKey` will always see empty keys. It also skips `gnutls_privkey_deinit(privkey)` on the success path and never calls `gnutls_global_deinit()`, causing leaks and improper teardown. Please export the keys into the strings (e.g. via `gnutls_privkey_export2` or x509 helpers), and ensure both `privkey` and the global state are deinitialized on all paths.
</issue_to_address>

### Comment 2
<location> `src/iptux-core/internal/EncryptGnutls.cpp:13-22` </location>
<code_context>
+  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);
+  if (ret < 0) {
+    LOG_WARN("gnutls_privkey_generate failed: %s", gnutls_strerror(ret));
</code_context>

<issue_to_address>
**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:

```cpp
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) {

```

```cpp
  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;

```
</issue_to_address>

### Comment 3
<location> `src/iptux-core/ProgramData.cpp:84-85` </location>
<code_context>
     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->Save();
 }
</code_context>

<issue_to_address>
**🚨 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:

```cpp
  // 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.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 13 to 22
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;

Comment on lines +84 to +85
config->SetString("public_key", public_key);
config->SetString("private_key", private_key);
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.

@lidaobing lidaobing linked an issue Jan 5, 2026 that may be closed by this pull request
@github-actions
Copy link

github-actions bot commented Jan 6, 2026

Test Results

0 tests   - 66   0 ✅  - 66   0s ⏱️ -2s
0 suites  - 32   0 💤 ± 0 
0 files    -  1   0 ❌ ± 0 

Results for commit e1a1f09. ± Comparison against base commit 121d325.

♻️ This comment has been updated with latest results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

support encrypt message

2 participants