Modify omemo_set_device_list to check against the persistent
known_devices.txt file before notifying the user. This prevents
false-positive security alerts on every restart for devices
that have already been registered and trusted.
Check against the persistent known_devices.txt file before notifying
the user. This ensures that "New OMEMO device" alerts are only shown
for devices that are truly new to this client.
Ref: ce9b5140d
Removing the UI notification in omemo_start_device_session to reduce
noise during /omemo start.
The information is still available in the debug logs.
And I think for existing sessions its just too much without value.
Ref: b20e3f1a
When sending OMEMO messages, we print a warning message for every
participant without a device ID on every single send:
```
Can't find a OMEMO device id for my@jid.org
```
This clutters the UI due to the high volume of repetitive information.
This is solved by tracking suppressed warnings within the window
structure. A warning for a specific JID and type is only shown once
per window context.
Suppression state is stored in the base ProfWin structure and managed
via helper functions (win_warn_needed, win_warn_sent). The state is
lazily initialized to save resources and automatically cleaned up when
the window is closed.
Warnings are explicitly reset when an OMEMO session is started or
ended to ensure users see fresh alerts when toggling encryption.
In the process of trying to bring down the time of sending an OMEMO
encrypted message I noticed that a lot of time was spent in generating the
random key and IV (~0.54s on my machine).
By changing this to a single call to `gcry_random_bytes_secure()` I have
been able to slash the time by 50% to ~0.27s.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Instead of flushing the OMEMO store to disk after each operation, do it
only once after all recipients are processed.
A user reported a long delay between hitting enter on the keyboard and the
message being sent on the wire in an OMEMO-enabled group chat. This patch
hopefully improves this situation.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
a26cdf386b / #2104 added OMEMO trust status to the
title bar, but it was being re-calculated on UI refresh.
Update only on certain operations (like `/omemo trust`, new device
discoveries and even incoming OMEMO messages for example).
Add __attribute__((nonnull) hints to omemo_receive_message and
omemo_on_message_recv declarations to catch NULL arguments at
compiletime.
So we can set the error state unconditionally in the functions.
If an incoming OMEMO message failed to decrypt (due to missing session
keys or untrusted identities), Profanity would fall back to displaying
the raw XMPP body. This usually contained a generic string like "This
message is encrypted with OMEMO,".
We wrote the detailed reason in the debug logs but the user only saw the
fallback message and probably wondered *why* the message wasn't
displayed properly.
I'm unsure if we should display the fallback message as well though.
After typing `/omemo start` a user was not informed of what exactly
happens.
Profanity is fetching the device list and the key bundle for each
device. This happens asynchronously. It could be that it takes some time
and the user already starts typing a message, believing OMEMO is
working.
Now we print:
* Initiating OMEMO session..
* Fetching device list and key bundles
* OMEMO session ready
So the user understands much better the current situation.
The /omemo fingerprint command provided a list of all keys ever
seen for a contact but did not indicate which keys were currently "active"
(present in the latest server device list). This made it difficult for users
to identify which fingerprints actually required verification to fix
encryption issues.
Users can now easily distinguish between devices currently in use and
historical keys that no longer matter.
When encryption fails users can immediately see which "active" key is
"untrusted," allowing them to verify the correct fingerprint fast.
Function `omemo_is_device_active` will check if a fingerprint belongs
to a device currently announced by the contacts server.
When OMEMO encryption failed due to untrusted devices, the
user was presented with a generic error message. This made it difficult
to identify which specific fingerprints were causing the issue, forcing
users to manually hunt for untrusted keys using /omemo fingerprint.
New function `omemo_get_jid_untrusted_fingerprints` to specifically
identify unverified identities for a contact.
`omemo_on_message_send` will now catch encryption failures and
explicitly list the untrusted fingerprints in the chat window.
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.
Previously the titlebar only showed [OMEMO], regardless of whether the
active session contained untrusted devices. This forced users to manually
run /omemo fingerprint to check the security status of their conversation,
making it difficult to know at a glance if a session was truly secure.
New function `omemo_is_jid_trusted()` to check if all active devices for a JID are trusted.
Update chat and MUC titlebar to display [trusted] or [untrusted] tags when an OMEMO session is active.
Not sure if MUC makes sense here?
When an OMEMO bundle is received with an empty <prekeys/> element,
`prekeys_list` remains empty. In omemo_start_device_session(),
performing a modulo operation on the length of an empty list
(0) triggered a SIGFPE (Floating point exception).
torsocks likely affected the network environment where such empty
or incomplete bundles could be intercepted.
Fix https://github.com/profanity-im/profanity/issues/1997
When setting up OMEMO for the first time via `/omemo gen` one had
to reconnect in order to make OMEMO work. This is fixed now.
Fixes: 5b6b5130 ("Fix OMEMO keyfile loading")
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Several users have reported segfaults when starting up profanity which
has OMEMO support, but OMEMO is not set up yet.
@StefanKropp has been able to reproduce this and tracked it down to
`_load_identity()` calling `omemo_known_devices_keyfile_save()`.
The latter then calls `save_keyfile()` which calls
`g_key_file_save_to_file()`. This can then fail if one of the first two
strings is NULL and won't set the `error` on return. In its error handling
`save_keyfile()` unconditionally dereferences `error` which leads to the
segfault.
Fix this and also go through the entire codebase and verify that the usage
of `GError` is done correctly.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Instead of adding stuff to `_shutdown()`, we can now register a shutdown
routine from the respective `init()` function of our modules.
This also has the advantage, that we're sure they're called in reverse
order from how the initialization happened.
I didn't simply use `atexit()` because POSIX says that the number of
possible callbacks is limited (min 32) and I was not sure whether
we will maybe extend this number at one point.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
This reverts commit 3b099e9403.
This resulted in:
```
==5285== Invalid read of size 16
==5285== at 0x4FA80FC: ec_public_key_serialize (in /usr/lib64/libsignal-protocol-c.so.2.3.3)
==5285== by 0x4E5E76: omemo_identity_key (omemo.c:419)
==5285== by 0x4EBB7E: omemo_bundle_publish (omemo.c:129)
==5285== by 0x4E5BD9: omemo_publish_crypto_materials (omemo.c:335)
==5285== by 0x460407: sv_ev_connection_features_received (server_events.c:202)
==5285== by 0x43AA87: connection_features_received (connection.c:779)
==5285== by 0x4418C9: _disco_info_response_id_handler_onconnect (iq.c:2423)
==5285== by 0x43B9F1: _iq_handler (iq.c:241)
==5285== by 0x5163848: ??? (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x516A224: ??? (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x5E4FE43: ??? (in /usr/lib64/libxml2.so.2.12.8)
==5285== by 0x5E54927: xmlParseChunk (in /usr/lib64/libxml2.so.2.12.8)
==5285== by 0x5163450: xmpp_run_once (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x439797: connection_check_events (connection.c:162)
==5285== by 0x43894E: session_process_events (session.c:256)
==5285== by 0x4319FF: prof_run (profanity.c:128)
==5285== by 0x4EDAE6: main (main.c:174)
==5285== Address 0xa1cb1e0 is 16 bytes inside a block of size 72 free'd
==5285== at 0x484875B: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==5285== by 0x4395D0: _xfree (connection.c:110)
==5285== by 0x516A1A7: xmpp_stanza_release (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x516A16C: xmpp_stanza_release (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x516A16C: xmpp_stanza_release (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x516A230: ??? (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x5E4FE43: ??? (in /usr/lib64/libxml2.so.2.12.8)
==5285== by 0x5E54927: xmlParseChunk (in /usr/lib64/libxml2.so.2.12.8)
==5285== by 0x5163450: xmpp_run_once (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x439797: connection_check_events (connection.c:162)
==5285== by 0x43894E: session_process_events (session.c:256)
==5285== by 0x4319FF: prof_run (profanity.c:128)
==5285== by 0x4EDAE6: main (main.c:174)
==5285== Block was alloc'd at
==5285== at 0x4845794: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==5285== by 0x43958A: _xmalloc (connection.c:102)
==5285== by 0x516A0D1: xmpp_stanza_new (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x516BF34: ??? (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x5F10A17: ??? (in /usr/lib64/libxml2.so.2.12.8)
==5285== by 0x5E5481F: xmlParseChunk (in /usr/lib64/libxml2.so.2.12.8)
==5285== by 0x5163450: xmpp_run_once (in /usr/lib64/libstrophe.so.0.13.1)
==5285== by 0x439797: connection_check_events (connection.c:162)
==5285== by 0x43894E: session_process_events (session.c:256)
==5285== by 0x4319FF: prof_run (profanity.c:128)
==5285== by 0x4EDAE6: main (main.c:174)
```
Tested via sending OMEMO messages via 1:1 and in MUC.
libsignal does this properly, so there wouldn't be a real double free, but
it will `abort()`.
Instead of destroying the identity key on disconnect, already destroy it
after it has been put into the libsignal 'ratchet identity key pair'.
In the case where the key pair is initially generated, the public
and private parts are only `ref()`'ed once in [0].
In the case where the key pair is read from the disk, the public
and private parts are `ref()`'ed twice, first when decoded in [1] resp.
[2] and a second time in [3].
When `omemo_on_disconnect()` is called we were `unref()`'ing the parts
twice, before this patch. First in [4], a second time in [5] resp. [6].
Now we do the second `unref()` already when loading.
[0] `signal_protocol_key_helper_generate_identity_key_pair()`
[1] `curve_decode_point()`
[2] `curve_decode_private_point()`
[3] `ratchet_identity_key_pair_create()`
[4] `ratchet_identity_key_pair_destroy()`
[5] `ec_private_key_destroy()`
[6] `ec_public_key_destroy()`
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
* Fix missing destruction of `session_store` and mutex
* Replace `glib_hash_table_free()`
The glib API `g_hash_table_destroy()` does exactly the same.
* Use the default libsignal `destroy_func` instead of doing that manually
* Set internal state to `0` after everything is cleaned up
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
Use a singleton `Jid` inside the connection instead of always re-creating
a `Jid` from the same string.
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
We have to lazy load the keyfiles as they will maybe be written at one
point and should be initialized by that time.
Fixup of ca2df180d8
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>