From ce9b5140d5e6da31bd8c75874f840d28f06a7cd9 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Fri, 6 Mar 2026 23:40:51 +0100 Subject: [PATCH] feat: Notify users when new OMEMO devices or fingerprints are discovered Previously, Profanity silently updated OMEMO device lists and identity caches in the background. This often led to confusion when a contact added a new device, as encryption or decryption would eventually fail due to untrusted keys without any prior warning to the user. The `notifying` flag will distinguish between initial data loading and realtime discovery. We now print a message both when a contact publishes a new device ID or when a new fingerprint is discovered. --- src/omemo/omemo.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index a28b7340..4b310649 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -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_INT(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_INT(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); @@ -1744,6 +1782,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));