From 54bd4046f404cc485575f0d405dabc36b3dde387 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Sat, 4 Jul 2026 22:02:54 +0200 Subject: [PATCH 1/2] feat(ox): Add support for encrypted file sharing via HTTP upload Add support to send encrypted files when in an OX (XEP-0373) session. When a user has OX encryption enabled in a 1:1 chat window, invoking `/sendfile` will now trigger local e2e encryption of the file using AES-256-GCM from XEP-0454 OMEMO media sharing. The encrypted payload is uploaded to the HTTP Upload (XEP-0363) component. On successful completion, the download URL is formatted under the custom `aesgcm://` scheme with the appended IV and key fragment, and transmitted as a standard OX encrypted signcrypt message. So this means not only the link to the file is encrypted (OX) but the file itself is encrypted as well. The drawback is that now `/sendfile` only works if Profanity is built with OMEMO support since we use those routines. We might make this configurable: * upload encrypted file (omemo) + encrypt link (ox) * upload plain file + encrypt link (ox) Fixes: https://github.com/profanity-im/profanity/issues/2175 Signed-off-by: Michael Vetter --- src/command/cmd_funcs.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 2327a7c85..1477f9eb6 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4897,6 +4897,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } gboolean omemo_enabled = FALSE; + gboolean ox_enabled = FALSE; gboolean sendfile_enabled = TRUE; switch (window->type) { @@ -4912,6 +4913,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) ProfChatWin* chatwin = (ProfChatWin*)window; assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); omemo_enabled = chatwin->is_omemo == TRUE; + ox_enabled = chatwin->is_ox == TRUE; sendfile_enabled = !((chatwin->pgp_send == TRUE && !prefs_get_boolean(PREF_PGP_SENDFILE)) || (chatwin->is_otr == TRUE && !prefs_get_boolean(PREF_OTR_SENDFILE))); break; @@ -4936,7 +4938,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) goto out; } - if (omemo_enabled) { + if (omemo_enabled || ox_enabled) { #ifdef HAVE_OMEMO char* err = NULL; alt_scheme = OMEMO_AESGCM_URL_SCHEME; @@ -4949,6 +4951,15 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } goto out; } +#else + if (ox_enabled) { + cons_show_error("OX encrypted file sharing requires OMEMO to be enabled at compile time."); + win_println(window, THEME_ERROR, "-", "Sending encrypted files via OX is not possible without OMEMO support."); + if (fh) { + fclose(fh); + } + goto out; + } #endif } From 74673232ec3fc96583baa6c2a31acc3671c47a19 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Sat, 4 Jul 2026 23:02:21 +0200 Subject: [PATCH 2/2] feat(ox): Make AES-256-GCM file encryption optional for OX sendfile Add `/ox encryptfile on|off` command to allow users to toggle local AES-256-GCM encryption before file upload when using OX. Enabled by default when compiled with OMEMO support. Disabled in case we compile with OX but without OMEMO. Signed-off-by: Michael Vetter --- src/command/cmd_ac.c | 6 ++++++ src/command/cmd_defs.c | 2 ++ src/command/cmd_funcs.c | 19 +++++++++++++++---- src/config/preferences.c | 9 +++++++++ src/config/preferences.h | 1 + src/ui/console.c | 6 ++++++ 6 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index c2fa6c448..29c119ac1 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -985,6 +985,7 @@ cmd_ac_init(void) autocomplete_add(ox_ac, "announce"); autocomplete_add(ox_ac, "discover"); autocomplete_add(ox_ac, "request"); + autocomplete_add(ox_ac, "encryptfile"); autocomplete_add(ox_log_ac, "on"); autocomplete_add(ox_log_ac, "off"); @@ -2582,6 +2583,11 @@ _ox_autocomplete(ProfWin* window, const char* const input, gboolean previous) return found; } + found = autocomplete_param_with_func(input, "/ox encryptfile", prefs_autocomplete_boolean_choice, previous, NULL); + if (found) { + return found; + } + if (strncmp(input, "/ox announce ", 13) == 0) { return cmd_ac_complete_filepath(input, "/ox announce", previous); } diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 9861f94ad..b84ddee4a 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -1744,6 +1744,7 @@ static const struct cmd_t command_defs[] = { "/ox start []", "/ox end", "/ox log on|off|redact", + "/ox encryptfile on|off", "/ox char ", "/ox announce ", "/ox discover ", @@ -1760,6 +1761,7 @@ static const struct cmd_t command_defs[] = { { "end", "End PGP encrypted chat with the current recipient." }, { "log on|off", "Enable or disable plaintext logging of PGP encrypted messages." }, { "log redact", "Log PGP encrypted messages, but replace the contents with [redacted]." }, + { "encryptfile on|off", "Encrypt files locally using AES-GCM (XEP-0454) before uploading them when using OX." }, { "char ", "Set the character to be displayed next to PGP encrypted messages." }, { "announce ", "Announce a public key by pushing it on the XMPP Server" }, { "discover ", "Discover public keys of a jid. The OpenPGP Key IDs will be displayed" }, diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 1477f9eb6..84a0841f4 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4898,6 +4898,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) gboolean omemo_enabled = FALSE; gboolean ox_enabled = FALSE; + gboolean ox_encryptfile = FALSE; gboolean sendfile_enabled = TRUE; switch (window->type) { @@ -4914,6 +4915,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); omemo_enabled = chatwin->is_omemo == TRUE; ox_enabled = chatwin->is_ox == TRUE; + ox_encryptfile = prefs_get_boolean(PREF_OX_ENCRYPTFILE) == TRUE; sendfile_enabled = !((chatwin->pgp_send == TRUE && !prefs_get_boolean(PREF_PGP_SENDFILE)) || (chatwin->is_otr == TRUE && !prefs_get_boolean(PREF_OTR_SENDFILE))); break; @@ -4938,7 +4940,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) goto out; } - if (omemo_enabled || ox_enabled) { + if (omemo_enabled || (ox_enabled && ox_encryptfile)) { #ifdef HAVE_OMEMO char* err = NULL; alt_scheme = OMEMO_AESGCM_URL_SCHEME; @@ -4952,9 +4954,9 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) goto out; } #else - if (ox_enabled) { - cons_show_error("OX encrypted file sharing requires OMEMO to be enabled at compile time."); - win_println(window, THEME_ERROR, "-", "Sending encrypted files via OX is not possible without OMEMO support."); + if (ox_enabled && ox_encryptfile) { + cons_show_error("OX encrypted file sharing requires OMEMO to be enabled at compile time (for AES-GCM file encryption helper)."); + win_println(window, THEME_ERROR, "-", "To send raw files with only the link encrypted, run '/ox encryptfile off'."); if (fh) { fclose(fh); } @@ -7579,6 +7581,15 @@ cmd_ox(ProfWin* window, const char* const command, gchar** args) return TRUE; } + else if (g_strcmp0(args[0], "encryptfile") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + return TRUE; + } + _cmd_set_boolean_preference(args[1], "Encrypting files locally using AES-GCM before sending via OX", PREF_OX_ENCRYPTFILE); + return TRUE; + } + // The '/ox keys' command - same like in pgp // Should we move this to a common command // e.g. '/openpgp keys'?. diff --git a/src/config/preferences.c b/src/config/preferences.c index f92d49524..f56a473fa 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1941,6 +1941,7 @@ _get_group(preference_t pref) case PREF_OMEMO_TRUST_MODE: return PREF_GROUP_OMEMO; case PREF_OX_LOG: + case PREF_OX_ENCRYPTFILE: return PREF_GROUP_OX; case PREF_SPELLCHECK_ENABLE: case PREF_SPELLCHECK_LANG: @@ -2222,6 +2223,8 @@ _get_key(preference_t pref) return "stamp.incoming"; case PREF_OX_LOG: return "log"; + case PREF_OX_ENCRYPTFILE: + return "encryptfile"; case PREF_MOOD: return "mood"; case PREF_VCARD_PHOTO_CMD: @@ -2293,6 +2296,12 @@ _get_default_boolean(preference_t pref) case PREF_PGP_PUBKEY_AUTOIMPORT: default: return FALSE; + case PREF_OX_ENCRYPTFILE: +#ifdef HAVE_OMEMO + return TRUE; +#else + return FALSE; +#endif } } diff --git a/src/config/preferences.h b/src/config/preferences.h index 76934bbf3..47052aa26 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -155,6 +155,7 @@ typedef enum { PREF_INCOMING_STAMP, PREF_NOTIFY_ROOM_OFFLINE, PREF_OX_LOG, + PREF_OX_ENCRYPTFILE, PREF_MOOD, PREF_STROPHE_VERBOSITY, PREF_STROPHE_SM_ENABLED, diff --git a/src/ui/console.c b/src/ui/console.c index 5ad9019dc..24ef80129 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2277,6 +2277,12 @@ cons_show_ox_prefs(void) auto_gchar gchar* ch = prefs_get_ox_char(); cons_show("OX char (/ox char) : %s", ch); + if (prefs_get_boolean(PREF_OX_ENCRYPTFILE)) { + cons_show("OX encrypt file (/ox encryptfile): ON"); + } else { + cons_show("OX encrypt file (/ox encryptfile): OFF"); + } + cons_alert(NULL); }