diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 2fe05234..98aa8af6 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1014,13 +1014,21 @@ 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); + + if (error) { + *error = OMEMO_ERR_NONE; + } + if (!from) { log_error("[OMEMO][RECV] Invalid jid %s", from_jid); + if (error) { + *error = OMEMO_ERR_INVALID_JID; + } return NULL; } @@ -1036,6 +1044,9 @@ 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"); + if (error) { + *error = OMEMO_ERR_NO_KEY; + } return NULL; } @@ -1052,6 +1063,9 @@ 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"); + if (error) { + *error = OMEMO_ERR_MUC_SENDER_NOT_FOUND; + } return NULL; } } else { @@ -1069,6 +1083,9 @@ 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"); + if (error) { + *error = OMEMO_ERR_NO_SESSION; + } return NULL; } @@ -1127,13 +1144,23 @@ 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 (error) { + 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); + if (error) { + *error = OMEMO_ERR_DECRYPT_FAILED; + } return NULL; } @@ -1146,6 +1173,9 @@ 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); + if (error) { + *error = OMEMO_ERR_DECRYPT_FAILED; + } return NULL; } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 47ebaaf7..62d28197 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -98,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); 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); diff --git a/src/xmpp/message.c b/src/xmpp/message.c index bc07b073..4562cc87 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -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 diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index d41588a6..d53b83d7 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -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,10 @@ omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted) char* iv_text = NULL; char* payload_text = NULL; + if (error) { + *error = OMEMO_ERR_NONE; + } + xmpp_stanza_t* encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); if (!encrypted) { return NULL; @@ -367,40 +371,64 @@ omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted) xmpp_stanza_t* header = xmpp_stanza_get_child_by_name(encrypted, "header"); if (!header) { + if (error) { + *error = OMEMO_ERR_OTHER; + } return NULL; } const char* sid_text = xmpp_stanza_get_attribute(header, "sid"); if (!sid_text) { + if (error) { + *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) { + if (error) { + *error = OMEMO_ERR_OTHER; + } return NULL; } iv_text = xmpp_stanza_get_text(iv); if (!iv_text) { + if (error) { + *error = OMEMO_ERR_OTHER; + } return NULL; } size_t iv_len; iv_raw = g_base64_decode(iv_text, &iv_len); if (!iv_raw) { + if (error) { + *error = OMEMO_ERR_OTHER; + } return NULL; } xmpp_stanza_t* payload = xmpp_stanza_get_child_by_name(encrypted, "payload"); if (!payload) { + if (error) { + *error = OMEMO_ERR_OTHER; + } goto quit; } payload_text = xmpp_stanza_get_text(payload); if (!payload_text) { + if (error) { + *error = OMEMO_ERR_OTHER; + } goto quit; } size_t payload_len; payload_raw = g_base64_decode(payload_text, &payload_len); if (!payload_raw) { + if (error) { + *error = OMEMO_ERR_OTHER; + } goto quit; } @@ -439,7 +467,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); diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index 9f29cb96..e26029e1 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -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); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 5fd214c9..620fb66e 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -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; diff --git a/tests/unittests/omemo/stub_omemo.c b/tests/unittests/omemo/stub_omemo.c index c445eb23..7d1afd5f 100644 --- a/tests/unittests/omemo/stub_omemo.c +++ b/tests/unittests/omemo/stub_omemo.c @@ -129,6 +129,27 @@ 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) +{ + if (error) { + *error = OMEMO_ERR_NONE; + } + return NULL; +} + +char* +omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted, omemo_error_t* error) +{ + if (error) { + *error = OMEMO_ERR_NONE; + } + return NULL; +} + uint32_t omemo_device_id() {