Merge pull request #2104 from profanity-im/omemo-ux

Improve OMEMO UX
This commit is contained in:
Michael Vetter
2026-03-09 09:15:24 +01:00
committed by GitHub
10 changed files with 461 additions and 20 deletions

View File

@@ -2332,13 +2332,18 @@ static const struct cmd_t command_defs[] = {
"/omemo clear_device_list",
"/omemo qrcode")
CMD_DESC(
"OMEMO commands to manage keys, and perform encryption during chat sessions.")
"OMEMO commands to manage keys, and perform encryption during chat sessions.\n"
"The title bar will show the OMEMO session status:\n"
"[OMEMO][trusted] - All active devices for the contact are trusted.\n"
"[OMEMO][untrusted] - One or more active devices for the contact are untrusted.\n")
CMD_ARGS(
{ "gen", "Generate OMEMO cryptographic materials for current account." },
{ "start [<contact>]", "Start an OMEMO session with contact, or current recipient if omitted." },
{ "end", "End the current OMEMO session." },
{ "log on|off", "Enable or disable plaintext logging of OMEMO encrypted messages." },
{ "log redact", "Log OMEMO encrypted messages, but replace the contents with [redacted]." },
{ "trust [<contact>] <fp>", "Trust a fingerprint for a contact, or current recipient if omitted. If all active devices are trusted, the title bar will show [trusted]. Otherwise, it will show [untrusted]." },
{ "untrust [<contact>] <fp>","Untrust a fingerprint for a contact, or current recipient if omitted." },
{ "fingerprint [<contact>]", "Show contact's fingerprints, or current recipient's if omitted." },
{ "char <char>", "Set the character to be displayed next to OMEMO encrypted messages." },
{ "trustmode manual", "Set the global OMEMO trust mode to manual, OMEMO keys has to be trusted manually." },

View File

@@ -8479,6 +8479,7 @@ cmd_omemo_start(ProfWin* window, const char* const command, gchar** args)
}
accounts_add_omemo_state(session_get_account_name(), chatwin->barejid, TRUE);
win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "Initiating OMEMO session with %s...", chatwin->barejid);
omemo_start_session(chatwin->barejid);
chatwin->is_omemo = TRUE;
} else if (window->type == WIN_MUC) {
@@ -8493,6 +8494,7 @@ cmd_omemo_start(ProfWin* window, const char* const command, gchar** args)
if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS
&& muc_member_type(mucwin->roomjid) == MUC_MEMBER_TYPE_MEMBERS_ONLY) {
accounts_add_omemo_state(session_get_account_name(), mucwin->roomjid, TRUE);
win_println((ProfWin*)mucwin, THEME_DEFAULT, "!", "Initiating OMEMO session in %s...", mucwin->roomjid);
omemo_start_muc_sessions(mucwin->roomjid);
mucwin->is_omemo = TRUE;
} else {
@@ -8686,8 +8688,9 @@ cmd_omemo_fingerprint(ProfWin* window, const char* const command, gchar** args)
for (GList* fingerprint = fingerprints; fingerprint != NULL; fingerprint = fingerprint->next) {
auto_char char* formatted_fingerprint = omemo_format_fingerprint(fingerprint->data);
gboolean trusted = omemo_is_trusted_identity(jid->barejid, fingerprint->data);
gboolean active = omemo_is_device_active(jid->barejid, fingerprint->data);
win_println(window, THEME_DEFAULT, "-", "%s's OMEMO fingerprint: %s%s", jid->barejid, formatted_fingerprint, trusted ? " (trusted)" : "");
win_println(window, THEME_DEFAULT, "-", "%s's OMEMO fingerprint: %s%s%s", jid->barejid, formatted_fingerprint, trusted ? " (trusted)" : " (untrusted)", active ? " (active)" : " (inactive)");
}
g_list_free(fingerprints);

View File

@@ -105,6 +105,7 @@ typedef struct omemo_context
prof_keyfile_t knowndevices;
GHashTable* known_devices;
gboolean loaded;
gboolean notifying;
} omemo_context;
static omemo_context omemo_ctx;
@@ -168,6 +169,8 @@ _omemo_finalize_identity_load(ProfAccount* account)
omemo_devicelist_subscribe();
omemo_ctx.notifying = TRUE;
return TRUE;
}
@@ -254,6 +257,7 @@ omemo_on_connect(ProfAccount* account)
signal_protocol_store_context_set_identity_key_store(omemo_ctx.store, &identity_key_store);
omemo_ctx.loaded = FALSE;
omemo_ctx.notifying = FALSE;
omemo_ctx.device_list = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_list_free);
omemo_ctx.device_list_handler = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
omemo_ctx.known_devices = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_hash_table_destroy);
@@ -515,6 +519,40 @@ omemo_set_device_list(const char* const from, GList* device_list)
log_debug("[OMEMO] Setting device list for %s", STR_MAYBE_NULL(from));
auto_jid Jid* jid = jid_create(from ?: connection_get_fulljid());
if (omemo_ctx.notifying) {
GList* old_list = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid);
GList* new_iter;
for (new_iter = device_list; new_iter != NULL; new_iter = new_iter->next) {
uint32_t dev_id = GPOINTER_TO_UINT(new_iter->data);
gboolean found = FALSE;
GList* old_iter;
for (old_iter = old_list; old_iter != NULL; old_iter = old_iter->next) {
if (GPOINTER_TO_UINT(old_iter->data) == dev_id) {
found = TRUE;
break;
}
}
if (!found) {
if (equals_our_barejid(jid->barejid)) {
if (dev_id != omemo_ctx.device_id) {
cons_show("New OMEMO device (ID: %u) added to your account.", dev_id);
}
} else {
ProfChatWin* chatwin = wins_get_chat(jid->barejid);
if (chatwin) {
win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "New OMEMO device (ID: %u) found for %s.", dev_id, jid->barejid);
} else {
ProfMucWin* mucwin = wins_get_muc(jid->barejid);
if (mucwin) {
win_println((ProfWin*)mucwin, THEME_DEFAULT, "!", "New OMEMO device (ID: %u) found for %s.", dev_id, jid->barejid);
}
}
}
}
}
}
g_hash_table_insert(omemo_ctx.device_list, strdup(jid->barejid), device_list);
OmemoDeviceListHandler handler = g_hash_table_lookup(omemo_ctx.device_list_handler, jid->barejid);
@@ -691,8 +729,30 @@ omemo_start_device_session(const char* const jid, uint32_t device_id,
}
log_debug("[OMEMO] create session with %s device %d", jid, device_id);
if (omemo_ctx.notifying) {
ProfChatWin* chatwin = wins_get_chat(jid);
if (chatwin) {
win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "OMEMO session with %s (device %u) ready.", jid, device_id);
} else {
ProfMucWin* mucwin = wins_get_muc(jid);
if (mucwin) {
win_println((ProfWin*)mucwin, THEME_DEFAULT, "!", "OMEMO session with %s (device %u) ready.", jid, device_id);
}
}
}
} else {
log_debug("[OMEMO] session with %s device %d exists", jid, device_id);
if (omemo_ctx.notifying) {
ProfChatWin* chatwin = wins_get_chat(jid);
if (chatwin) {
win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "OMEMO session with %s (device %u) already exists.", jid, device_id);
} else {
ProfMucWin* mucwin = wins_get_muc(jid);
if (mucwin) {
win_println((ProfWin*)mucwin, THEME_DEFAULT, "!", "OMEMO session with %s (device %u) already exists.", jid, device_id);
}
}
}
}
out:
@@ -837,10 +897,42 @@ omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_
// Don't send the message if no key could be encrypted.
// (Since none of the recipients would be able to read the message.)
if (keys == NULL) {
win_println(win, THEME_ERROR, "!", "This message cannot be decrypted for any recipient.\n"
"You should trust your recipients' device fingerprint(s) using \"/omemo trust FINGERPRINT\".\n"
"It could also be that the key bundle of the recipient(s) have not been received. "
"In this case, you could try \"omemo end\", \"omemo start\", and send the message again.");
win_println(win, THEME_ERROR, "!", "This message cannot be encrypted for any recipient.");
// Check for untrusted fingerprints for each recipient
GList* recipients = NULL;
if (muc) {
ProfMucWin* mucwin = (ProfMucWin*)win;
GList* members = muc_members(mucwin->roomjid);
GList* iter;
for (iter = members; iter != NULL; iter = iter->next) {
auto_jid Jid* jidp = jid_create(iter->data);
recipients = g_list_append(recipients, strdup(jidp->barejid));
}
g_list_free(members);
} else {
ProfChatWin* chatwin = (ProfChatWin*)win;
recipients = g_list_append(recipients, strdup(chatwin->barejid));
}
GList* rec_iter;
for (rec_iter = recipients; rec_iter != NULL; rec_iter = rec_iter->next) {
GList* untrusted = omemo_get_jid_untrusted_fingerprints(rec_iter->data);
if (untrusted) {
win_println(win, THEME_ERROR, "!", "Untrusted OMEMO fingerprints for %s:", (char*)rec_iter->data);
GList* fp_iter;
for (fp_iter = untrusted; fp_iter != NULL; fp_iter = fp_iter->next) {
char* formatted = omemo_format_fingerprint(fp_iter->data);
win_println(win, THEME_ERROR, "!", " - %s", formatted);
free(formatted);
}
win_println(win, THEME_ERROR, "!", "Use \"/omemo trust %s <fingerprint>\" to trust them.", (char*)rec_iter->data);
g_list_free_full(untrusted, free);
}
}
g_list_free_full(recipients, free);
win_println(win, THEME_ERROR, "!", "It could also be that key bundles have not been received. Try \"/omemo end\", then \"/omemo start\".");
goto out;
}
@@ -922,13 +1014,17 @@ out:
char*
omemo_on_message_recv(const char* const from_jid, uint32_t sid,
const unsigned char* const iv, size_t iv_len, GList* keys,
const unsigned char* const payload, size_t payload_len, gboolean muc, gboolean* trusted)
const unsigned char* const payload, size_t payload_len, gboolean muc, gboolean* trusted, omemo_error_t* error)
{
unsigned char* plaintext = NULL;
auto_jid Jid* sender = NULL;
auto_jid Jid* from = jid_create(from_jid);
*error = OMEMO_ERR_NONE;
if (!from) {
log_error("[OMEMO][RECV] Invalid jid %s", from_jid);
*error = OMEMO_ERR_INVALID_JID;
return NULL;
}
@@ -944,6 +1040,7 @@ omemo_on_message_recv(const char* const from_jid, uint32_t sid,
if (!key) {
log_warning("[OMEMO][RECV] received a message with no corresponding key");
*error = OMEMO_ERR_NO_KEY;
return NULL;
}
@@ -960,6 +1057,7 @@ omemo_on_message_recv(const char* const from_jid, uint32_t sid,
g_list_free(roster);
if (!sender) {
log_warning("[OMEMO][RECV] cannot find MUC message sender fulljid");
*error = OMEMO_ERR_MUC_SENDER_NOT_FOUND;
return NULL;
}
} else {
@@ -977,6 +1075,7 @@ omemo_on_message_recv(const char* const from_jid, uint32_t sid,
res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal);
if (res != 0) {
log_error("[OMEMO][RECV] cannot create session cipher");
*error = OMEMO_ERR_NO_SESSION;
return NULL;
}
@@ -1035,13 +1134,19 @@ omemo_on_message_recv(const char* const from_jid, uint32_t sid,
session_cipher_free(cipher);
if (res != 0) {
log_error("[OMEMO][RECV] cannot decrypt message key");
log_error("[OMEMO][RECV] cannot decrypt message key: %d", res);
if (!*trusted) {
*error = OMEMO_ERR_NOT_TRUSTED;
} else {
*error = OMEMO_ERR_DECRYPT_FAILED;
}
return NULL;
}
if (signal_buffer_len(plaintext_key) != AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH) {
log_error("[OMEMO][RECV] invalid key length");
signal_buffer_free(plaintext_key);
*error = OMEMO_ERR_DECRYPT_FAILED;
return NULL;
}
@@ -1054,6 +1159,7 @@ omemo_on_message_recv(const char* const from_jid, uint32_t sid,
if (res != 0) {
log_error("[OMEMO][RECV] cannot decrypt message: %s", gcry_strerror(res));
free(plaintext);
*error = OMEMO_ERR_DECRYPT_FAILED;
return NULL;
}
@@ -1137,7 +1243,7 @@ omemo_is_trusted_identity(const char* const jid, const char* const fingerprint)
signal_protocol_address address = {
.name = jid,
.name_len = strlen(jid),
.device_id = GPOINTER_TO_INT(device_id),
.device_id = GPOINTER_TO_UINT(device_id),
};
size_t fingerprint_len;
@@ -1147,7 +1253,7 @@ omemo_is_trusted_identity(const char* const jid, const char* const fingerprint)
buffer = signal_buffer_append(buffer, fingerprint_raw, fingerprint_len);
gboolean trusted = is_trusted_identity(&address, signal_buffer_data(buffer), signal_buffer_len(buffer), &omemo_ctx.identity_key_store);
log_debug("[OMEMO] Device trusted %s (%d): %d", jid, GPOINTER_TO_INT(device_id), trusted);
log_debug("[OMEMO] Device trusted %s (%u): %d", jid, GPOINTER_TO_UINT(device_id), trusted);
free(fingerprint_raw);
signal_buffer_free(buffer);
@@ -1155,6 +1261,127 @@ omemo_is_trusted_identity(const char* const jid, const char* const fingerprint)
return trusted;
}
gboolean
omemo_is_jid_trusted(const char* const jid)
{
if (!omemo_loaded()) {
return FALSE;
}
GList* device_list = g_hash_table_lookup(omemo_ctx.device_list, jid);
if (!device_list) {
return FALSE;
}
GList* device_id_iter;
for (device_id_iter = device_list; device_id_iter != NULL; device_id_iter = device_id_iter->next) {
uint32_t device_id = GPOINTER_TO_UINT(device_id_iter->data);
if (device_id == omemo_ctx.device_id && equals_our_barejid(jid)) {
continue;
}
GHashTable* known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid);
if (!known_identities) {
return FALSE;
}
gboolean found = FALSE;
GList* fp_list = g_hash_table_get_keys(known_identities);
GList* fp_iter;
for (fp_iter = fp_list; fp_iter != NULL; fp_iter = fp_iter->next) {
if (device_id == GPOINTER_TO_UINT(g_hash_table_lookup(known_identities, fp_iter->data))) {
if (!omemo_is_trusted_identity(jid, fp_iter->data)) {
g_list_free(fp_list);
return FALSE;
}
found = TRUE;
break;
}
}
g_list_free(fp_list);
if (!found) {
return FALSE;
}
}
return TRUE;
}
GList*
omemo_get_jid_untrusted_fingerprints(const char* const jid)
{
if (!omemo_loaded()) {
return NULL;
}
GList* device_list = g_hash_table_lookup(omemo_ctx.device_list, jid);
if (!device_list) {
return NULL;
}
GList* untrusted_fps = NULL;
GList* device_id_iter;
for (device_id_iter = device_list; device_id_iter != NULL; device_id_iter = device_id_iter->next) {
uint32_t device_id = GPOINTER_TO_UINT(device_id_iter->data);
if (device_id == omemo_ctx.device_id && equals_our_barejid(jid)) {
continue;
}
GHashTable* known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid);
if (!known_identities) {
continue;
}
GList* fp_list = g_hash_table_get_keys(known_identities);
GList* fp_iter;
for (fp_iter = fp_list; fp_iter != NULL; fp_iter = fp_iter->next) {
if (device_id == GPOINTER_TO_UINT(g_hash_table_lookup(known_identities, fp_iter->data))) {
if (!omemo_is_trusted_identity(jid, fp_iter->data)) {
untrusted_fps = g_list_append(untrusted_fps, g_strdup(fp_iter->data));
}
break;
}
}
g_list_free(fp_list);
}
return untrusted_fps;
}
gboolean
omemo_is_device_active(const char* const jid, const char* const fingerprint)
{
if (!omemo_loaded()) {
return FALSE;
}
GList* device_list = g_hash_table_lookup(omemo_ctx.device_list, jid);
if (!device_list) {
return FALSE;
}
GHashTable* known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid);
if (!known_identities) {
return FALSE;
}
void* device_id = g_hash_table_lookup(known_identities, fingerprint);
if (!device_id) {
return FALSE;
}
uint32_t dev_id = GPOINTER_TO_UINT(device_id);
GList* iter;
for (iter = device_list; iter != NULL; iter = iter->next) {
if (GPOINTER_TO_UINT(iter->data) == dev_id) {
return TRUE;
}
}
return FALSE;
}
static char*
_omemo_fingerprint(ec_public_key* identity, gboolean formatted)
{
@@ -1697,6 +1924,27 @@ _cache_device_identity(const char* const jid, uint32_t device_id, ec_public_key*
if (!fingerprint) {
return;
}
if (omemo_ctx.notifying) {
if (!g_hash_table_contains(known_identities, fingerprint)) {
char* formatted = omemo_format_fingerprint(fingerprint);
if (equals_our_barejid(jid)) {
cons_show("New OMEMO fingerprint for your account: %s (ID: %u).", formatted, device_id);
} else {
ProfChatWin* chatwin = wins_get_chat(jid);
if (chatwin) {
win_println((ProfWin*)chatwin, THEME_DEFAULT, "!", "New OMEMO fingerprint for %s: %s (ID: %u).", jid, formatted, device_id);
} else {
ProfMucWin* mucwin = wins_get_muc(jid);
if (mucwin) {
win_println((ProfWin*)mucwin, THEME_DEFAULT, "!", "New OMEMO fingerprint for %s: %s (ID: %u).", jid, formatted, device_id);
}
}
}
free(formatted);
}
}
log_debug("[OMEMO] cache identity for %s:%d: %s", jid, device_id, fingerprint);
g_hash_table_insert(known_identities, strdup(fingerprint), GINT_TO_POINTER(device_id));

View File

@@ -85,6 +85,9 @@ void omemo_trust(const char* const jid, const char* const fingerprint);
void omemo_untrust(const char* const jid, const char* const fingerprint);
GList* omemo_known_device_identities(const char* const jid);
gboolean omemo_is_trusted_identity(const char* const jid, const char* const fingerprint);
gboolean omemo_is_jid_trusted(const char* const jid);
GList* omemo_get_jid_untrusted_fingerprints(const char* const jid);
gboolean omemo_is_device_active(const char* const jid, const char* const fingerprint);
char* omemo_fingerprint_autocomplete(const char* const search_str, gboolean previous, void* context);
void omemo_fingerprint_autocomplete_reset(void);
gboolean omemo_automatic_start(const char* const recipient);
@@ -95,8 +98,9 @@ void omemo_start_muc_sessions(const char* const roomjid);
void omemo_start_device_session(const char* const jid, uint32_t device_id, GList* prekeys, uint32_t signed_prekey_id, const unsigned char* const signed_prekey, size_t signed_prekey_len, const unsigned char* const signature, size_t signature_len, const unsigned char* const identity_key, size_t identity_key_len);
gboolean omemo_loaded(void);
char* omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_receipt, gboolean muc, const char* const replace_id);
char* omemo_on_message_recv(const char* const from, uint32_t sid, const unsigned char* const iv, size_t iv_len, GList* keys, const unsigned char* const payload, size_t payload_len, gboolean muc, gboolean* trusted);
char* omemo_on_message_recv(const char* const from, uint32_t sid, const unsigned char* const iv, size_t iv_len, GList* keys, const unsigned char* const payload, size_t payload_len, gboolean muc, gboolean* trusted, omemo_error_t* error) __attribute__((nonnull(9, 10)));
char* omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res);
gcry_error_t omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment);

View File

@@ -49,6 +49,9 @@
#include "ui/window_list.h"
#include "ui/window.h"
#include "ui/screen.h"
#ifdef HAVE_OMEMO
#include "omemo/omemo.h"
#endif
#include "xmpp/roster_list.h"
#include "xmpp/chat_session.h"
@@ -410,6 +413,10 @@ _show_muc_privacy(ProfMucWin* mucwin)
{
int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET);
int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED);
#ifdef HAVE_OMEMO
int trusted_attrs = theme_attrs(THEME_TITLE_TRUSTED);
int untrusted_attrs = theme_attrs(THEME_TITLE_UNTRUSTED);
#endif
if (mucwin->is_omemo) {
wprintw(win, " ");
@@ -423,6 +430,32 @@ _show_muc_privacy(ProfMucWin* mucwin)
wprintw(win, "]");
wattroff(win, bracket_attrs);
#ifdef HAVE_OMEMO
if (omemo_is_jid_trusted(mucwin->roomjid)) {
wprintw(win, " ");
wattron(win, bracket_attrs);
wprintw(win, "[");
wattroff(win, bracket_attrs);
wattron(win, trusted_attrs);
wprintw(win, "trusted");
wattroff(win, trusted_attrs);
wattron(win, bracket_attrs);
wprintw(win, "]");
wattroff(win, bracket_attrs);
} else {
wprintw(win, " ");
wattron(win, bracket_attrs);
wprintw(win, "[");
wattroff(win, bracket_attrs);
wattron(win, untrusted_attrs);
wprintw(win, "untrusted");
wattroff(win, untrusted_attrs);
wattron(win, bracket_attrs);
wprintw(win, "]");
wattroff(win, bracket_attrs);
}
#endif
return;
}
@@ -552,6 +585,32 @@ _show_privacy(ProfChatWin* chatwin)
wprintw(win, "]");
wattroff(win, bracket_attrs);
#ifdef HAVE_OMEMO
if (omemo_is_jid_trusted(chatwin->barejid)) {
wprintw(win, " ");
wattron(win, bracket_attrs);
wprintw(win, "[");
wattroff(win, bracket_attrs);
wattron(win, trusted_attrs);
wprintw(win, "trusted");
wattroff(win, trusted_attrs);
wattron(win, bracket_attrs);
wprintw(win, "]");
wattroff(win, bracket_attrs);
} else {
wprintw(win, " ");
wattron(win, bracket_attrs);
wprintw(win, "[");
wattroff(win, bracket_attrs);
wattron(win, untrusted_attrs);
wprintw(win, "untrusted");
wattroff(win, untrusted_attrs);
wattron(win, bracket_attrs);
wprintw(win, "]");
wattroff(win, bracket_attrs);
}
#endif
return;
}

View File

@@ -356,6 +356,7 @@ message_init(void)
ProfMessage* message = g_new0(ProfMessage, 1);
message->enc = PROF_MSG_ENC_NONE;
message->omemo_err = OMEMO_ERR_NONE;
message->trusted = true;
message->type = PROF_MSG_TYPE_UNINITIALIZED;
@@ -1110,8 +1111,29 @@ _handle_groupchat(xmpp_stanza_t* const stanza)
// check omemo encryption
#ifdef HAVE_OMEMO
message->plain = omemo_receive_message(stanza, &message->trusted);
if (message->plain != NULL) {
message->plain = omemo_receive_message(stanza, &message->trusted, &message->omemo_err);
if (message->omemo_err != OMEMO_ERR_NONE) {
message->enc = PROF_MSG_ENC_OMEMO;
if (message->plain == NULL) {
switch (message->omemo_err) {
case OMEMO_ERR_NO_KEY:
message->plain = g_strdup("OMEMO message received but no key for this device found.");
break;
case OMEMO_ERR_NOT_TRUSTED:
message->plain = g_strdup("OMEMO message received but sender identity is untrusted.");
break;
case OMEMO_ERR_NO_SESSION:
message->plain = g_strdup("OMEMO message received but no session found. Try '/omemo start'.");
break;
case OMEMO_ERR_DECRYPT_FAILED:
message->plain = g_strdup("OMEMO message received but decryption failed.");
break;
default:
message->plain = g_strdup("OMEMO message received but could not be decrypted.");
break;
}
}
} else if (message->plain != NULL) {
message->enc = PROF_MSG_ENC_OMEMO;
}
#endif
@@ -1270,8 +1292,29 @@ _handle_muc_private_message(xmpp_stanza_t* const stanza)
// check omemo encryption
#ifdef HAVE_OMEMO
message->plain = omemo_receive_message(stanza, &message->trusted);
if (message->plain != NULL) {
message->plain = omemo_receive_message(stanza, &message->trusted, &message->omemo_err);
if (message->omemo_err != OMEMO_ERR_NONE) {
message->enc = PROF_MSG_ENC_OMEMO;
if (message->plain == NULL) {
switch (message->omemo_err) {
case OMEMO_ERR_NO_KEY:
message->plain = g_strdup("OMEMO message received but no key for this device found.");
break;
case OMEMO_ERR_NOT_TRUSTED:
message->plain = g_strdup("OMEMO message received but sender identity is untrusted.");
break;
case OMEMO_ERR_NO_SESSION:
message->plain = g_strdup("OMEMO message received but no session found. Try '/omemo start'.");
break;
case OMEMO_ERR_DECRYPT_FAILED:
message->plain = g_strdup("OMEMO message received but decryption failed.");
break;
default:
message->plain = g_strdup("OMEMO message received but could not be decrypted.");
break;
}
}
} else if (message->plain != NULL) {
message->enc = PROF_MSG_ENC_OMEMO;
}
#endif
@@ -1437,8 +1480,29 @@ _handle_chat(xmpp_stanza_t* const stanza, gboolean is_mam, gboolean is_carbon, c
#ifdef HAVE_OMEMO
// check omemo encryption
message->plain = omemo_receive_message(stanza, &message->trusted);
if (message->plain != NULL) {
message->plain = omemo_receive_message(stanza, &message->trusted, &message->omemo_err);
if (message->omemo_err != OMEMO_ERR_NONE) {
message->enc = PROF_MSG_ENC_OMEMO;
if (message->plain == NULL) {
switch (message->omemo_err) {
case OMEMO_ERR_NO_KEY:
message->plain = g_strdup("OMEMO message received but no key for this device found.");
break;
case OMEMO_ERR_NOT_TRUSTED:
message->plain = g_strdup("OMEMO message received but sender identity is untrusted.");
break;
case OMEMO_ERR_NO_SESSION:
message->plain = g_strdup("OMEMO message received but no session found. Try '/omemo start'.");
break;
case OMEMO_ERR_DECRYPT_FAILED:
message->plain = g_strdup("OMEMO message received but decryption failed.");
break;
default:
message->plain = g_strdup("OMEMO message received but could not be decrypted.");
break;
}
}
} else if (message->plain != NULL) {
message->enc = PROF_MSG_ENC_OMEMO;
}
#endif

View File

@@ -350,7 +350,7 @@ out:
}
char*
omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted)
omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted, omemo_error_t* error)
{
char* plaintext = NULL;
const char* type = xmpp_stanza_get_type(stanza);
@@ -360,6 +360,8 @@ omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted)
char* iv_text = NULL;
char* payload_text = NULL;
*error = OMEMO_ERR_NONE;
xmpp_stanza_t* encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO);
if (!encrypted) {
return NULL;
@@ -367,40 +369,48 @@ omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted)
xmpp_stanza_t* header = xmpp_stanza_get_child_by_name(encrypted, "header");
if (!header) {
*error = OMEMO_ERR_OTHER;
return NULL;
}
const char* sid_text = xmpp_stanza_get_attribute(header, "sid");
if (!sid_text) {
*error = OMEMO_ERR_OTHER;
return NULL;
}
uint32_t sid = strtoul(sid_text, NULL, 10);
xmpp_stanza_t* iv = xmpp_stanza_get_child_by_name(header, "iv");
if (!iv) {
*error = OMEMO_ERR_OTHER;
return NULL;
}
iv_text = xmpp_stanza_get_text(iv);
if (!iv_text) {
*error = OMEMO_ERR_OTHER;
return NULL;
}
size_t iv_len;
iv_raw = g_base64_decode(iv_text, &iv_len);
if (!iv_raw) {
*error = OMEMO_ERR_OTHER;
return NULL;
}
xmpp_stanza_t* payload = xmpp_stanza_get_child_by_name(encrypted, "payload");
if (!payload) {
*error = OMEMO_ERR_OTHER;
goto quit;
}
payload_text = xmpp_stanza_get_text(payload);
if (!payload_text) {
*error = OMEMO_ERR_OTHER;
goto quit;
}
size_t payload_len;
payload_raw = g_base64_decode(payload_text, &payload_len);
if (!payload_raw) {
*error = OMEMO_ERR_OTHER;
goto quit;
}
@@ -439,7 +449,7 @@ omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted)
plaintext = omemo_on_message_recv(from, sid, iv_raw, iv_len,
keys, payload_raw, payload_len,
g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0, trusted);
g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0, trusted, error);
if (keys) {
g_list_free_full(keys, (GDestroyNotify)omemo_key_free);

View File

@@ -44,4 +44,5 @@ void omemo_devicelist_request(const char* const jid);
void omemo_bundle_publish(gboolean first);
void omemo_bundle_request(const char* const jid, uint32_t device_id, ProfIqCallback func, ProfIqFreeCallback free_func, void* userdata);
int omemo_start_device_session_handle_bundle(xmpp_stanza_t* const stanza, void* const userdata);
char* omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted);
char* omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted, omemo_error_t* error) __attribute__((nonnull(2, 3)));

View File

@@ -150,6 +150,17 @@ typedef enum {
PROF_MSG_TYPE_MUCPM
} prof_msg_type_t;
typedef enum {
OMEMO_ERR_NONE = 0,
OMEMO_ERR_NO_KEY,
OMEMO_ERR_NOT_TRUSTED,
OMEMO_ERR_NO_SESSION,
OMEMO_ERR_DECRYPT_FAILED,
OMEMO_ERR_INVALID_JID,
OMEMO_ERR_MUC_SENDER_NOT_FOUND,
OMEMO_ERR_OTHER
} omemo_error_t;
typedef struct prof_message_t
{
Jid* from_jid;
@@ -172,6 +183,7 @@ typedef struct prof_message_t
char* plain;
GDateTime* timestamp;
prof_enc_t enc;
omemo_error_t omemo_err;
gboolean trusted;
gboolean is_mam;
prof_msg_type_t type;

View File

@@ -42,12 +42,30 @@ omemo_is_trusted_identity(const char* const jid, const char* const fingerprint)
return TRUE;
}
gboolean
omemo_is_jid_trusted(const char* const jid)
{
return TRUE;
}
GList*
omemo_get_jid_untrusted_fingerprints(const char* const jid)
{
return NULL;
}
GList*
omemo_known_device_identities(const char* const jid)
{
return NULL;
}
gboolean
omemo_is_device_active(const char* const jid, const char* const fingerprint)
{
return TRUE;
}
gboolean
omemo_loaded(void)
{
@@ -111,6 +129,23 @@ omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res)
};
void omemo_free(void* a) {};
char*
omemo_on_message_recv(const char* const from, uint32_t sid,
const unsigned char* const iv, size_t iv_len,
GList* keys, const unsigned char* const payload,
size_t payload_len, gboolean muc, gboolean* trusted, omemo_error_t* error)
{
*error = OMEMO_ERR_NONE;
return NULL;
}
char*
omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted, omemo_error_t* error)
{
*error = OMEMO_ERR_NONE;
return NULL;
}
uint32_t
omemo_device_id()
{