From 4a5b672f95266990549ddc7eb2366a99cfddeefa Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Feb 2019 06:24:47 +0140 Subject: Link against libsignal-protocol-c --- Makefile.am | 8 ++++++++ configure.ac | 13 +++++++++++++ src/command/cmd_defs.c | 20 +++++++++++++++++++- src/command/cmd_funcs.c | 23 +++++++++++++++++++++++ src/command/cmd_funcs.h | 2 ++ src/omemo/omemo.c | 10 ++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/omemo/omemo.c diff --git a/Makefile.am b/Makefile.am index c1784f32..226f9bad 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,6 +69,7 @@ unittest_sources = \ src/ui/ui.h \ src/otr/otr.h \ src/pgp/gpg.h \ + src/omemo/omemo.h \ src/command/cmd_defs.h src/command/cmd_defs.c \ src/command/cmd_funcs.h src/command/cmd_funcs.c \ src/command/cmd_ac.h src/command/cmd_ac.c \ @@ -168,6 +169,9 @@ otr3_sources = \ otr4_sources = \ src/otr/otrlib.h src/otr/otrlibv4.c src/otr/otr.h src/otr/otr.c +omemo_sources = \ + src/omemo/omemo.h src/omemo/omemo.c + if BUILD_PYTHON_API core_sources += $(python_sources) unittest_sources += $(python_sources) @@ -204,6 +208,10 @@ core_sources += $(otr4_sources) endif endif +if BUILD_OMEMO +core_sources += $(omemo_sources) +endif + AM_CFLAGS = @AM_CFLAGS@ -I$(srcdir)/src bin_PROGRAMS = profanity diff --git a/configure.ac b/configure.ac index 0b8e4512..9cce9840 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,8 @@ AC_ARG_ENABLE([otr], [AS_HELP_STRING([--enable-otr], [enable otr encryption])]) AC_ARG_ENABLE([pgp], [AS_HELP_STRING([--enable-pgp], [enable pgp])]) +AC_ARG_ENABLE([omemo], + [AS_HELP_STRING([--enable-omemo], [enable omemo encryption])]) AC_ARG_WITH([xscreensaver], [AS_HELP_STRING([--with-xscreensaver], [use libXScrnSaver to determine idle time])]) AC_ARG_WITH([themes], @@ -264,6 +266,17 @@ if test "x$enable_otr" != xno; then [AC_MSG_NOTICE([libotr not found, otr encryption support not enabled])])]) fi +AM_CONDITIONAL([BUILD_OMEMO], [false]) +if test "x$enable_omemo" != xno; then + AC_CHECK_LIB([signal-protocol-c], [signal_context_create], + [AM_CONDITIONAL([BUILD_OMEMO], [true]) + AC_DEFINE([HAVE_LIBSIGNAL_PROTOCOL], [1], [Have omemo]), + LIBS="-lsignal-protocol-c $LIBS"], + [AS_IF([test "x$enable_omemo" = xyes], + [AC_MSG_ERROR([libsignal-protocol-c is required for omemo support])], + [AC_MSG_NOTICE([libsignal-protocol-c not found, omemo support not enabled])])]) +fi + AS_IF([test "x$with_themes" = xno], [THEMES_INSTALL="false"], [THEMES_INSTALL="true"]) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 4447020b..23ab2ebf 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2328,7 +2328,25 @@ static struct cmd_t command_defs[] = CMD_EXAMPLES( "/cmd list", "/cmd exec ping") - } + }, + + { "/omemo", + parse_args, 1, 3, NULL, + CMD_SUBFUNCS( + { "init", cmd_omemo_init }) + CMD_NOMAINFUNC + CMD_TAGS( + CMD_TAG_CHAT, + CMD_TAG_UI) + CMD_SYN( + "/omemo init") + CMD_DESC( + "Omemo commands to manage keys, and perform encryption during chat sessions.") + CMD_ARGS( + { "init", "Initialize omemo" }) + CMD_EXAMPLES( + "/omemo init") + }, }; static GHashTable *search_index; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index b2f0ee7f..97ce2025 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -85,6 +85,10 @@ #include "pgp/gpg.h" #endif +#ifdef HAVE_LIBSIGNAL_PROTOCOL +#include "omemo/omemo.h" +#endif + #ifdef HAVE_GTK #include "ui/tray.h" #endif @@ -7872,3 +7876,22 @@ _cmd_set_boolean_preference(gchar *arg, const char *const command, g_string_free(enabled, TRUE); g_string_free(disabled, TRUE); } + +gboolean +cmd_omemo_init(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_LIBSIGNAL_PROTOCOL + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to initialize omemo"); + return TRUE; + } + + ProfAccount *account = accounts_get_account(session_get_account_name()); + omemo_init(account); + cons_show("Initialized omemo"); + return TRUE; +#else + cons_show("This version of Profanity has not been built with Omemo support enabled"); + return TRUE; +#endif +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 89166ba1..fb81721d 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -214,4 +214,6 @@ gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args) gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); +gboolean cmd_omemo_init(ProfWin *window, const char *const command, gchar **args); + #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c new file mode 100644 index 00000000..a0da5639 --- /dev/null +++ b/src/omemo/omemo.c @@ -0,0 +1,10 @@ +#include + +#include "config/account.h" + +void +omemo_init(ProfAccount *account) +{ + signal_context *global_context; + signal_context_create(&global_context, NULL); +} -- cgit 1.4.1-2-gfad0 From bfbc8edcad16e576041d7a81a4d58c70fa17a3fa Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Feb 2019 19:18:15 +0140 Subject: Add crypto backend for signal-protocol --- Makefile.am | 3 +- configure.ac | 8 ++++ src/omemo/crypto.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/omemo/crypto.h | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/omemo/omemo.c | 34 +++++++++++++- src/omemo/omemo.h | 3 ++ 6 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 src/omemo/crypto.c create mode 100644 src/omemo/crypto.h create mode 100644 src/omemo/omemo.h diff --git a/Makefile.am b/Makefile.am index 226f9bad..8bfffd7a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,6 +70,7 @@ unittest_sources = \ src/otr/otr.h \ src/pgp/gpg.h \ src/omemo/omemo.h \ + src/omemo/crypto.h \ src/command/cmd_defs.h src/command/cmd_defs.c \ src/command/cmd_funcs.h src/command/cmd_funcs.c \ src/command/cmd_ac.h src/command/cmd_ac.c \ @@ -170,7 +171,7 @@ otr4_sources = \ src/otr/otrlib.h src/otr/otrlibv4.c src/otr/otr.h src/otr/otr.c omemo_sources = \ - src/omemo/omemo.h src/omemo/omemo.c + src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c if BUILD_PYTHON_API core_sources += $(python_sources) diff --git a/configure.ac b/configure.ac index 9cce9840..5ba70464 100644 --- a/configure.ac +++ b/configure.ac @@ -275,6 +275,14 @@ if test "x$enable_omemo" != xno; then [AS_IF([test "x$enable_omemo" = xyes], [AC_MSG_ERROR([libsignal-protocol-c is required for omemo support])], [AC_MSG_NOTICE([libsignal-protocol-c not found, omemo support not enabled])])]) + + AC_CHECK_LIB([sodium], [sodium_init], + [AM_CONDITIONAL([BUILD_OMEMO], [true]) + AC_DEFINE([HAVE_LIBSIGNAL_PROTOCOL], [1], [Have omemo]), + LIBS="-lsodium $LIBS"], + [AS_IF([test "x$enable_omemo" = xyes], + [AC_MSG_ERROR([libsodium is required for omemo support])], + [AC_MSG_NOTICE([libsodium not found, omemo support not enabled])])]) fi AS_IF([test "x$with_themes" = xno], diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c new file mode 100644 index 00000000..a986c729 --- /dev/null +++ b/src/omemo/crypto.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +#include "omemo/crypto.h" + +int +omemo_crypto_init(void) +{ + if (sodium_init() < 0) { + return -1; + } + + if (crypto_aead_aes256gcm_is_available() == 0) { + return -1; + } + + return 0; +} + +int +omemo_random_func(uint8_t *data, size_t len, void *user_data) +{ + randombytes_buf(data, len); + return 0; +} + +int +omemo_hmac_sha256_init_func(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) +{ + *hmac_context = sodium_malloc(sizeof(crypto_auth_hmacsha256_state)); + return crypto_auth_hmacsha256_init(*hmac_context, key, key_len); +} + +int +omemo_hmac_sha256_update_func(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data) +{ + return crypto_auth_hmacsha256_update(hmac_context, data, data_len); +} + +int +omemo_hmac_sha256_final_func(void *hmac_context, signal_buffer **output, void *user_data) +{ + int ret; + unsigned char out[crypto_auth_hmacsha256_BYTES]; + + if ((ret = crypto_auth_hmacsha256_final(hmac_context, out)) != 0) { + return ret; + } + + *output = signal_buffer_create(out, crypto_auth_hmacsha256_BYTES); + return 0; +} + +void +omemo_hmac_sha256_cleanup_func(void *hmac_context, void *user_data) +{ + sodium_free(hmac_context); +} + +int +omemo_sha512_digest_init_func(void **digest_context, void *user_data) +{ + *digest_context = sodium_malloc(sizeof(crypto_hash_sha512_state)); + return crypto_hash_sha512_init(*digest_context); +} + +int +omemo_sha512_digest_update_func(void *digest_context, const uint8_t *data, size_t data_len, void *user_data) +{ + return crypto_hash_sha512_update(digest_context, data, data_len); +} + +int +omemo_sha512_digest_final_func(void *digest_context, signal_buffer **output, void *user_data) +{ + int ret; + unsigned char out[crypto_hash_sha512_BYTES]; + + if ((ret = crypto_hash_sha512_final(digest_context, out)) != 0) { + return ret; + } + + *output = signal_buffer_create(out, crypto_hash_sha512_BYTES); + return 0; +} + +void +omemo_sha512_digest_cleanup_func(void *digest_context, void *user_data) +{ + sodium_free(digest_context); +} + +int +omemo_encrypt_func(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, + const uint8_t *plaintext, size_t plaintext_len, void *user_data) +{ + unsigned char *ciphertext; + unsigned long long ciphertext_len; + + assert(cipher != SG_CIPHER_AES_GCM_NOPADDING); + assert(key_len == crypto_aead_aes256gcm_KEYBYTES); + assert(iv_len == crypto_aead_aes256gcm_NPUBBYTES); + + ciphertext = malloc(plaintext_len + crypto_aead_aes256gcm_ABYTES); + crypto_aead_aes256gcm_encrypt(ciphertext, &ciphertext_len, plaintext, plaintext_len, NULL, 0, NULL, iv, key); + + *output = signal_buffer_create(ciphertext, ciphertext_len); + free(ciphertext); + + return 0; +} + +int +omemo_decrypt_func(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, + const uint8_t *ciphertext, size_t ciphertext_len, void *user_data) +{ + unsigned char *plaintext; + unsigned long long plaintext_len; + + assert(cipher != SG_CIPHER_AES_GCM_NOPADDING); + assert(key_len == crypto_aead_aes256gcm_KEYBYTES); + assert(iv_len == crypto_aead_aes256gcm_NPUBBYTES); + + plaintext = malloc(ciphertext_len - crypto_aead_aes256gcm_ABYTES); + if (crypto_aead_aes256gcm_decrypt(plaintext, &plaintext_len, NULL, ciphertext, ciphertext_len, NULL, 0, iv, key) < 0) { + free(plaintext); + return -1; + } + + *output = signal_buffer_create(plaintext, plaintext_len); + free(plaintext); + + return 0; +} diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h new file mode 100644 index 00000000..759cef69 --- /dev/null +++ b/src/omemo/crypto.h @@ -0,0 +1,136 @@ +#include + +#define SG_CIPHER_AES_GCM_NOPADDING 1000 + +int omemo_crypto_init(void); +/** +* Callback for a secure random number generator. +* This function shall fill the provided buffer with random bytes. +* +* @param data pointer to the output buffer +* @param len size of the output buffer +* @return 0 on success, negative on failure +*/ +int omemo_random_func(uint8_t *data, size_t len, void *user_data); + +/** +* Callback for an HMAC-SHA256 implementation. +* This function shall initialize an HMAC context with the provided key. +* +* @param hmac_context private HMAC context pointer +* @param key pointer to the key +* @param key_len length of the key +* @return 0 on success, negative on failure +*/ +int omemo_hmac_sha256_init_func(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data); + +/** +* Callback for an HMAC-SHA256 implementation. +* This function shall update the HMAC context with the provided data +* +* @param hmac_context private HMAC context pointer +* @param data pointer to the data +* @param data_len length of the data +* @return 0 on success, negative on failure +*/ +int omemo_hmac_sha256_update_func(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data); + +/** +* Callback for an HMAC-SHA256 implementation. +* This function shall finalize an HMAC calculation and populate the output +* buffer with the result. +* +* @param hmac_context private HMAC context pointer +* @param output buffer to be allocated and populated with the result +* @return 0 on success, negative on failure +*/ +int omemo_hmac_sha256_final_func(void *hmac_context, signal_buffer **output, void *user_data); + +/** +* Callback for an HMAC-SHA256 implementation. +* This function shall free the private context allocated in +* hmac_sha256_init_func. +* +* @param hmac_context private HMAC context pointer +*/ +void omemo_hmac_sha256_cleanup_func(void *hmac_context, void *user_data); + +/** +* Callback for a SHA512 message digest implementation. +* This function shall initialize a digest context. +* +* @param digest_context private digest context pointer +* @return 0 on success, negative on failure +*/ +int omemo_sha512_digest_init_func(void **digest_context, void *user_data); + +/** +* Callback for a SHA512 message digest implementation. +* This function shall update the digest context with the provided data. +* +* @param digest_context private digest context pointer +* @param data pointer to the data +* @param data_len length of the data +* @return 0 on success, negative on failure +*/ +int omemo_sha512_digest_update_func(void *digest_context, const uint8_t *data, size_t data_len, void *user_data); + +/** +* Callback for a SHA512 message digest implementation. +* This function shall finalize the digest calculation, populate the +* output buffer with the result, and prepare the context for reuse. +* +* @param digest_context private digest context pointer +* @param output buffer to be allocated and populated with the result +* @return 0 on success, negative on failure +*/ +int omemo_sha512_digest_final_func(void *digest_context, signal_buffer **output, void *user_data); + +/** +* Callback for a SHA512 message digest implementation. +* This function shall free the private context allocated in +* sha512_digest_init_func. +* +* @param digest_context private digest context pointer +*/ +void omemo_sha512_digest_cleanup_func(void *digest_context, void *user_data); + +/** +* Callback for an AES encryption implementation. +* +* @param output buffer to be allocated and populated with the ciphertext +* @param cipher specific cipher variant to use, either SG_CIPHER_AES_CTR_NOPADDING or SG_CIPHER_AES_CBC_PKCS5 +* @param key the encryption key +* @param key_len length of the encryption key +* @param iv the initialization vector +* @param iv_len length of the initialization vector +* @param plaintext the plaintext to encrypt +* @param plaintext_len length of the plaintext +* @return 0 on success, negative on failure +*/ +int omemo_encrypt_func(signal_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *plaintext, size_t plaintext_len, + void *user_data); + +/** +* Callback for an AES decryption implementation. +* +* @param output buffer to be allocated and populated with the plaintext +* @param cipher specific cipher variant to use, either SG_CIPHER_AES_CTR_NOPADDING or SG_CIPHER_AES_CBC_PKCS5 +* @param key the encryption key +* @param key_len length of the encryption key +* @param iv the initialization vector +* @param iv_len length of the initialization vector +* @param ciphertext the ciphertext to decrypt +* @param ciphertext_len length of the ciphertext +* @return 0 on success, negative on failure +*/ +int omemo_decrypt_func(signal_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *ciphertext, size_t ciphertext_len, + void *user_data); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index a0da5639..871f3bfc 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1,10 +1,40 @@ #include #include "config/account.h" +#include "ui/ui.h" +#include "omemo/crypto.h" void omemo_init(ProfAccount *account) { - signal_context *global_context; - signal_context_create(&global_context, NULL); + signal_context *global_context; + signal_crypto_provider crypto_provider = { + .random_func = omemo_random_func, + .hmac_sha256_init_func = omemo_hmac_sha256_init_func, + .hmac_sha256_update_func = omemo_hmac_sha256_update_func, + .hmac_sha256_final_func = omemo_hmac_sha256_final_func, + .hmac_sha256_cleanup_func = omemo_hmac_sha256_cleanup_func, + .sha512_digest_init_func = omemo_sha512_digest_init_func, + .sha512_digest_update_func = omemo_sha512_digest_update_func, + .sha512_digest_final_func = omemo_sha512_digest_final_func, + .sha512_digest_cleanup_func = omemo_sha512_digest_cleanup_func, + .encrypt_func = omemo_encrypt_func, + .decrypt_func = omemo_decrypt_func, + .user_data = NULL + }; + + if (omemo_crypto_init() != 0) { + cons_show("Error initializing Omemo crypto"); + } + + if (signal_context_create(&global_context, NULL) != 0) { + cons_show("Error initializing Omemo context"); + return; + } + + if (signal_context_set_crypto_provider(global_context, &crypto_provider) != 0) { + cons_show("Error initializing Omemo crypto"); + return; + } + //signal_context_set_locking_functions(global_context, lock_function, unlock_function); } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h new file mode 100644 index 00000000..2ca1c221 --- /dev/null +++ b/src/omemo/omemo.h @@ -0,0 +1,3 @@ +#include "config/account.h" + +void omemo_init(ProfAccount *account); -- cgit 1.4.1-2-gfad0 From 519cf295f31d77bec45644424f7212e9fb2692e8 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Feb 2019 19:38:40 +0140 Subject: Add signal-protocol locking --- src/omemo/omemo.c | 38 ++++++++++++++++++++++++++++++++++---- src/omemo/omemo.h | 2 ++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 871f3bfc..96e07d1c 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1,13 +1,24 @@ +#include #include #include "config/account.h" #include "ui/ui.h" +#include "omemo/omemo.h" #include "omemo/crypto.h" +static void lock(void *user_data); +static void unlock(void *user_data); + +struct omemo_context_t { + pthread_mutexattr_t attr; + pthread_mutex_t lock; +}; + void omemo_init(ProfAccount *account) { - signal_context *global_context; + signal_context *signal_ctx; + omemo_context *ctx = malloc(sizeof(omemo_context)); signal_crypto_provider crypto_provider = { .random_func = omemo_random_func, .hmac_sha256_init_func = omemo_hmac_sha256_init_func, @@ -27,14 +38,33 @@ omemo_init(ProfAccount *account) cons_show("Error initializing Omemo crypto"); } - if (signal_context_create(&global_context, NULL) != 0) { + pthread_mutexattr_init(&ctx->attr); + pthread_mutexattr_settype(&ctx->attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&ctx->lock, &ctx->attr); + + if (signal_context_create(&signal_ctx, ctx) != 0) { cons_show("Error initializing Omemo context"); return; } - if (signal_context_set_crypto_provider(global_context, &crypto_provider) != 0) { + if (signal_context_set_crypto_provider(signal_ctx, &crypto_provider) != 0) { cons_show("Error initializing Omemo crypto"); return; } - //signal_context_set_locking_functions(global_context, lock_function, unlock_function); + + signal_context_set_locking_functions(signal_ctx, lock, unlock); +} + +static void +lock(void *user_data) +{ + omemo_context *ctx = (omemo_context *)user_data; + pthread_mutex_lock(&ctx->lock); +} + +static void +unlock(void *user_data) +{ + omemo_context *ctx = (omemo_context *)user_data; + pthread_mutex_unlock(&ctx->lock); } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 2ca1c221..d34b90bf 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -1,3 +1,5 @@ #include "config/account.h" +typedef struct omemo_context_t omemo_context; + void omemo_init(ProfAccount *account); -- cgit 1.4.1-2-gfad0 From 2602cbf7852fa4317d961316b601a383b9077079 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Feb 2019 20:03:50 +0140 Subject: Move OMEMO initialization to profanity intialization Also store identity keys into account --- configure.ac | 4 ++-- src/command/cmd_defs.c | 11 +++++++---- src/command/cmd_funcs.c | 13 ++++++------- src/command/cmd_funcs.h | 2 +- src/omemo/omemo.c | 24 ++++++++++++++++++++---- src/omemo/omemo.h | 3 ++- src/profanity.c | 7 +++++++ src/ui/win_types.h | 1 + src/ui/window.c | 1 + 9 files changed, 47 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index 5ba70464..e886510e 100644 --- a/configure.ac +++ b/configure.ac @@ -270,7 +270,7 @@ AM_CONDITIONAL([BUILD_OMEMO], [false]) if test "x$enable_omemo" != xno; then AC_CHECK_LIB([signal-protocol-c], [signal_context_create], [AM_CONDITIONAL([BUILD_OMEMO], [true]) - AC_DEFINE([HAVE_LIBSIGNAL_PROTOCOL], [1], [Have omemo]), + AC_DEFINE([HAVE_OMEMO], [1], [Have omemo]), LIBS="-lsignal-protocol-c $LIBS"], [AS_IF([test "x$enable_omemo" = xyes], [AC_MSG_ERROR([libsignal-protocol-c is required for omemo support])], @@ -278,7 +278,7 @@ if test "x$enable_omemo" != xno; then AC_CHECK_LIB([sodium], [sodium_init], [AM_CONDITIONAL([BUILD_OMEMO], [true]) - AC_DEFINE([HAVE_LIBSIGNAL_PROTOCOL], [1], [Have omemo]), + AC_DEFINE([HAVE_OMEMO], [1], [Have omemo]), LIBS="-lsodium $LIBS"], [AS_IF([test "x$enable_omemo" = xyes], [AC_MSG_ERROR([libsodium is required for omemo support])], diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 23ab2ebf..af30db34 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2333,19 +2333,22 @@ static struct cmd_t command_defs[] = { "/omemo", parse_args, 1, 3, NULL, CMD_SUBFUNCS( - { "init", cmd_omemo_init }) + { "gen", cmd_omemo_gen }) CMD_NOMAINFUNC CMD_TAGS( CMD_TAG_CHAT, CMD_TAG_UI) CMD_SYN( - "/omemo init") + "/omemo gen", + "/omemo start []") CMD_DESC( "Omemo commands to manage keys, and perform encryption during chat sessions.") CMD_ARGS( - { "init", "Initialize omemo" }) + { "gen", "Generate OMEMO crytographic materials for current account." }, + { "start []", "Start an OMEMO session with contact, or current recipient if omitted." }) CMD_EXAMPLES( - "/omemo init") + "/omemo gen", + "/omemo start buddy@buddychat.org") }, }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 97ce2025..08aa88df 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -85,7 +85,7 @@ #include "pgp/gpg.h" #endif -#ifdef HAVE_LIBSIGNAL_PROTOCOL +#ifdef HAVE_OMEMO #include "omemo/omemo.h" #endif @@ -7878,20 +7878,19 @@ _cmd_set_boolean_preference(gchar *arg, const char *const command, } gboolean -cmd_omemo_init(ProfWin *window, const char *const command, gchar **args) +cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args) { -#ifdef HAVE_LIBSIGNAL_PROTOCOL +#ifdef HAVE_OMEMO if (connection_get_status() != JABBER_CONNECTED) { - cons_show("You must be connected with an account to initialize omemo"); + cons_show("You must be connected with an account to initialize OMEMO"); return TRUE; } ProfAccount *account = accounts_get_account(session_get_account_name()); - omemo_init(account); - cons_show("Initialized omemo"); + omemo_generate_crypto_materials(account); return TRUE; #else - cons_show("This version of Profanity has not been built with Omemo support enabled"); + cons_show("This version of Profanity has not been built with OMEMO support enabled"); return TRUE; #endif } diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index fb81721d..650c9eaf 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -214,6 +214,6 @@ gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args) gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); -gboolean cmd_omemo_init(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 96e07d1c..1b2998b0 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -8,6 +8,7 @@ static void lock(void *user_data); static void unlock(void *user_data); +static void omemo_load_crypto_materials(ProfAccount *account); struct omemo_context_t { pthread_mutexattr_t attr; @@ -15,8 +16,9 @@ struct omemo_context_t { }; void -omemo_init(ProfAccount *account) +omemo_init(void) { + log_info("Initialising OMEMO"); signal_context *signal_ctx; omemo_context *ctx = malloc(sizeof(omemo_context)); signal_crypto_provider crypto_provider = { @@ -35,7 +37,7 @@ omemo_init(ProfAccount *account) }; if (omemo_crypto_init() != 0) { - cons_show("Error initializing Omemo crypto"); + cons_show("Error initializing OMEMO crypto"); } pthread_mutexattr_init(&ctx->attr); @@ -43,18 +45,32 @@ omemo_init(ProfAccount *account) pthread_mutex_init(&ctx->lock, &ctx->attr); if (signal_context_create(&signal_ctx, ctx) != 0) { - cons_show("Error initializing Omemo context"); + cons_show("Error initializing OMEMO context"); return; } if (signal_context_set_crypto_provider(signal_ctx, &crypto_provider) != 0) { - cons_show("Error initializing Omemo crypto"); + cons_show("Error initializing OMEMO crypto"); return; } signal_context_set_locking_functions(signal_ctx, lock, unlock); } +void +omemo_generate_crypto_materials(ProfAccount *account) +{ + ratchet_identity_key_pair *identity_key_pair; + uint32_t registration_id; + signal_protocol_key_helper_pre_key_list_node *pre_keys_head; + session_signed_pre_key *signed_pre_key; + + signal_protocol_key_helper_generate_identity_key_pair(&identity_key_pair, global_context); + signal_protocol_key_helper_generate_registration_id(®istration_id, 0, global_context); + signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start_id, 100, global_context); + signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, identity_key_pair, 5, timestamp, global_context); +} + static void lock(void *user_data) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index d34b90bf..825529b1 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -2,4 +2,5 @@ typedef struct omemo_context_t omemo_context; -void omemo_init(ProfAccount *account); +void omemo_init(void); +void omemo_generate_crypto_materials(ProfAccount *account); diff --git a/src/profanity.c b/src/profanity.c index 1d4a2c35..f21f02c0 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -80,6 +80,10 @@ #include "pgp/gpg.h" #endif +#ifdef HAVE_OMEMO +#include "omemo/omemo.h" +#endif + static void _init(char *log_level); static void _shutdown(void); static void _connect_default(const char * const account); @@ -196,6 +200,9 @@ _init(char *log_level) #endif #ifdef HAVE_LIBGPGME p_gpg_init(); +#endif +#ifdef HAVE_OMEMO + omemo_init(); #endif atexit(_shutdown); plugins_init(); diff --git a/src/ui/win_types.h b/src/ui/win_types.h index 92618a36..6fe2811a 100644 --- a/src/ui/win_types.h +++ b/src/ui/win_types.h @@ -152,6 +152,7 @@ typedef struct prof_chat_win_t { gboolean otr_is_trusted; gboolean pgp_send; gboolean pgp_recv; + gboolean is_omemo; char *resource_override; gboolean history_shown; unsigned long memcheck; diff --git a/src/ui/window.c b/src/ui/window.c index cc2c2062..5693e35f 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -143,6 +143,7 @@ win_create_chat(const char *const barejid) new_win->otr_is_trusted = FALSE; new_win->pgp_recv = FALSE; new_win->pgp_send = FALSE; + new_win->is_omemo = FALSE; new_win->history_shown = FALSE; new_win->unread = 0; new_win->state = chat_state_new(); -- cgit 1.4.1-2-gfad0 From bce19811283ae4aa6f919497579704de32e30877 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 21 Feb 2019 06:44:47 +0140 Subject: Add devicelist subscription --- Makefile.am | 3 ++- src/xmpp/omemo.c | 15 +++++++++++++++ src/xmpp/omemo.h | 1 + src/xmpp/session.c | 7 +++++++ src/xmpp/stanza.c | 25 +++++++++++++++++++++++++ src/xmpp/stanza.h | 2 ++ 6 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/xmpp/omemo.c create mode 100644 src/xmpp/omemo.h diff --git a/Makefile.am b/Makefile.am index 8bfffd7a..d5e92bce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -171,7 +171,8 @@ otr4_sources = \ src/otr/otrlib.h src/otr/otrlibv4.c src/otr/otr.h src/otr/otr.c omemo_sources = \ - src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c + src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c \ + src/xmpp/omemo.h src/xmpp/omemo.c if BUILD_PYTHON_API core_sources += $(python_sources) diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c new file mode 100644 index 00000000..42b18c6b --- /dev/null +++ b/src/xmpp/omemo.c @@ -0,0 +1,15 @@ +#include "xmpp/connection.h" +#include "xmpp/iq.h" +#include "xmpp/stanza.h" + +void +omemo_devicelist_publish(void) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + xmpp_stanza_t *iq = stanza_create_omemo_devicelist_subscription(ctx, barejid); + iq_send_stanza(iq); + xmpp_stanza_release(iq); + + free(barejid); +} diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h new file mode 100644 index 00000000..413aa563 --- /dev/null +++ b/src/xmpp/omemo.h @@ -0,0 +1 @@ +void omemo_devicelist_publish(void); diff --git a/src/xmpp/session.c b/src/xmpp/session.c index de7fb7ac..ee836090 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -60,6 +60,10 @@ #include "xmpp/chat_session.h" #include "xmpp/jid.h" +#ifdef HAVE_OMEMO +#include "xmpp/omemo.h" +#endif + // for auto reconnect static struct { char *name; @@ -313,6 +317,9 @@ session_login_success(gboolean secured) roster_request(); bookmark_request(); blocking_request(); +#ifdef HAVE_OMEMO + omemo_devicelist_publish(); +#endif // items discovery char *domain = connection_get_domain(); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 534ee06b..ffd0c053 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2092,6 +2092,31 @@ stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, return iq; } +xmpp_stanza_t* +stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char *const jid) +{ + char *id = connection_create_stanza_id("omemo_devicelist_subscribe"); + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + free(id); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *subscribe = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(subscribe, "subscribe"); + xmpp_stanza_set_attribute(subscribe, "node", "eu.siacs.conversations.axolotl.devicelist"); + xmpp_stanza_set_attribute(subscribe, "jid", jid); + + xmpp_stanza_add_child(pubsub, subscribe); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(subscribe); + xmpp_stanza_release(pubsub); + + return iq; +} + static void _stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index d3c3c9dc..ce655a93 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -284,6 +284,8 @@ xmpp_stanza_t* stanza_create_room_kick_iq(xmpp_ctx_t *const ctx, const char *con xmpp_stanza_t* stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node); xmpp_stanza_t* stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, const char *const node, const char *const sessionid, DataForm *form); +xmpp_stanza_t* stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char *const jid); + int stanza_get_idle_time(xmpp_stanza_t *const stanza); void stanza_attach_priority(xmpp_ctx_t *const ctx, xmpp_stanza_t *const presence, const int pri); -- cgit 1.4.1-2-gfad0 From fdc5f25f2d1ba2a1b08a5c5b0ca41ed6395a1e76 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 21 Feb 2019 19:44:01 +0140 Subject: Add devicelist and bundle publication --- src/omemo/omemo.c | 134 +++++++++++++++++++++++++++++++++++++++++------- src/omemo/omemo.h | 10 ++++ src/xmpp/omemo.c | 44 +++++++++++++++- src/xmpp/omemo.h | 2 + src/xmpp/session.c | 6 ++- src/xmpp/stanza.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/xmpp/stanza.h | 5 +- 7 files changed, 323 insertions(+), 25 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 1b2998b0..080baf1b 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1,26 +1,41 @@ +#include + +#include #include +#include #include +#include #include "config/account.h" -#include "ui/ui.h" -#include "omemo/omemo.h" +#include "log.h" #include "omemo/crypto.h" +#include "omemo/omemo.h" +#include "ui/ui.h" +#include "xmpp/omemo.h" + +static gboolean loaded; static void lock(void *user_data); static void unlock(void *user_data); -static void omemo_load_crypto_materials(ProfAccount *account); struct omemo_context_t { pthread_mutexattr_t attr; pthread_mutex_t lock; + signal_context *signal; + uint32_t device_id; + GList *device_list; + ratchet_identity_key_pair *identity_key_pair; + uint32_t registration_id; + signal_protocol_key_helper_pre_key_list_node *pre_keys_head; + session_signed_pre_key *signed_pre_key; }; +static omemo_context omemo_ctx; + void omemo_init(void) { log_info("Initialising OMEMO"); - signal_context *signal_ctx; - omemo_context *ctx = malloc(sizeof(omemo_context)); signal_crypto_provider crypto_provider = { .random_func = omemo_random_func, .hmac_sha256_init_func = omemo_hmac_sha256_init_func, @@ -40,35 +55,116 @@ omemo_init(void) cons_show("Error initializing OMEMO crypto"); } - pthread_mutexattr_init(&ctx->attr); - pthread_mutexattr_settype(&ctx->attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&ctx->lock, &ctx->attr); + pthread_mutexattr_init(&omemo_ctx.attr); + pthread_mutexattr_settype(&omemo_ctx.attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&omemo_ctx.lock, &omemo_ctx.attr); - if (signal_context_create(&signal_ctx, ctx) != 0) { + if (signal_context_create(&omemo_ctx.signal, &omemo_ctx) != 0) { cons_show("Error initializing OMEMO context"); return; } - if (signal_context_set_crypto_provider(signal_ctx, &crypto_provider) != 0) { + if (signal_context_set_crypto_provider(omemo_ctx.signal, &crypto_provider) != 0) { cons_show("Error initializing OMEMO crypto"); return; } - signal_context_set_locking_functions(signal_ctx, lock, unlock); + signal_context_set_locking_functions(omemo_ctx.signal, lock, unlock); + + loaded = FALSE; + omemo_ctx.device_list = NULL; } void omemo_generate_crypto_materials(ProfAccount *account) { - ratchet_identity_key_pair *identity_key_pair; - uint32_t registration_id; - signal_protocol_key_helper_pre_key_list_node *pre_keys_head; - session_signed_pre_key *signed_pre_key; + omemo_ctx.device_id = randombytes_uniform(0x80000000); + omemo_ctx.device_list = g_list_append(omemo_ctx.device_list, GINT_TO_POINTER(omemo_ctx.device_id)); + signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); + signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); + signal_protocol_key_helper_generate_pre_keys(&omemo_ctx.pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); + + struct timeval tv; + gettimeofday(&tv, NULL); + unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; + signal_protocol_key_helper_generate_signed_pre_key(&omemo_ctx.signed_pre_key, omemo_ctx.identity_key_pair, 5, timestamp, omemo_ctx.signal); + + loaded = TRUE; + + omemo_devicelist_publish(); + omemo_bundle_publish(); +} + +void +omemo_start_session(ProfAccount *account, char *barejid) +{ + +} - signal_protocol_key_helper_generate_identity_key_pair(&identity_key_pair, global_context); - signal_protocol_key_helper_generate_registration_id(®istration_id, 0, global_context); - signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start_id, 100, global_context); - signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, identity_key_pair, 5, timestamp, global_context); +gboolean +omemo_loaded(void) +{ + return loaded; +} + +GList * const +omemo_device_list(void) +{ + return omemo_ctx.device_list; +} + +uint32_t +omemo_device_id(void) +{ + return omemo_ctx.device_id; +} + +void +omemo_identity_key(unsigned char **output, size_t *length) +{ + signal_buffer *buffer = NULL; + ec_public_key_serialize(&buffer, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair)); + *length = signal_buffer_len(buffer); + *output = malloc(*length); + memcpy(*output, signal_buffer_const_data(buffer), *length); + signal_buffer_free(buffer); +} + +void +omemo_signed_prekey(unsigned char **output, size_t *length) +{ + signal_buffer *buffer = NULL; + ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(omemo_ctx.signed_pre_key))); + *length = signal_buffer_len(buffer); + *output = malloc(*length); + memcpy(*output, signal_buffer_const_data(buffer), *length); + signal_buffer_free(buffer); +} + +void +omemo_signed_prekey_signature(unsigned char **output, size_t *length) +{ + *length = session_signed_pre_key_get_signature_len(omemo_ctx.signed_pre_key); + *output = malloc(*length); + memcpy(*output, session_signed_pre_key_get_signature(omemo_ctx.signed_pre_key), *length); +} + +void +omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths) +{ + signal_protocol_key_helper_pre_key_list_node *p; + for (p = omemo_ctx.pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { + session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); + signal_buffer *buffer = NULL; + ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_pre_key_get_key_pair(prekey))); + size_t length = signal_buffer_len(buffer); + unsigned char *prekey_value = malloc(length); + memcpy(prekey_value, signal_buffer_const_data(buffer), length); + signal_buffer_free(buffer); + *prekeys = g_list_append(*prekeys, prekey_value); + *ids = g_list_append(*ids, GINT_TO_POINTER(session_pre_key_get_id(prekey))); + *lengths = g_list_append(*lengths, GINT_TO_POINTER(length)); + } } static void diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 825529b1..20fd5d5d 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -4,3 +4,13 @@ typedef struct omemo_context_t omemo_context; void omemo_init(void); void omemo_generate_crypto_materials(ProfAccount *account); + +GList * const omemo_device_list(void); +uint32_t omemo_device_id(void); +void omemo_identity_key(unsigned char **output, size_t *length); +void omemo_signed_prekey(unsigned char **output, size_t *length); +void omemo_signed_prekey_signature(unsigned char **output, size_t *length); +void omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths); + +void omemo_start_session(ProfAccount *account, char *barejid); +gboolean omemo_loaded(void); diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 42b18c6b..a979ec90 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -2,14 +2,54 @@ #include "xmpp/iq.h" #include "xmpp/stanza.h" +#include "omemo/omemo.h" + void -omemo_devicelist_publish(void) +omemo_devicelist_subscribe(void) { xmpp_ctx_t * const ctx = connection_get_ctx(); char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); - xmpp_stanza_t *iq = stanza_create_omemo_devicelist_subscription(ctx, barejid); + xmpp_stanza_t *iq = stanza_create_omemo_devicelist_subscribe(ctx, barejid); iq_send_stanza(iq); xmpp_stanza_release(iq); free(barejid); } + +void +omemo_devicelist_publish(void) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, omemo_device_list()); + iq_send_stanza(iq); + xmpp_stanza_release(iq); +} + +void +omemo_bundle_publish(void) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + unsigned char *identity_key = NULL; + size_t identity_key_length; + unsigned char *signed_prekey = NULL; + size_t signed_prekey_length; + unsigned char *signed_prekey_signature = NULL; + size_t signed_prekey_signature_length; + GList *prekeys = NULL, *ids = NULL, *lengths = NULL; + + omemo_identity_key(&identity_key, &identity_key_length); + omemo_signed_prekey(&signed_prekey, &signed_prekey_length); + omemo_signed_prekey_signature(&signed_prekey_signature, &signed_prekey_signature_length); + omemo_prekeys(&prekeys, &ids, &lengths); + + xmpp_stanza_t *iq = stanza_create_omemo_bundle_publish(ctx, omemo_device_id(), + identity_key, identity_key_length, signed_prekey, signed_prekey_length, + signed_prekey_signature, signed_prekey_signature_length, + prekeys, ids, lengths); + iq_send_stanza(iq); + xmpp_stanza_release(iq); + + free(identity_key); + free(signed_prekey); + free(signed_prekey_signature); +} diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index 413aa563..eff3eee3 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -1 +1,3 @@ +void omemo_devicelist_subscribe(void); void omemo_devicelist_publish(void); +void omemo_bundle_publish(void); diff --git a/src/xmpp/session.c b/src/xmpp/session.c index ee836090..ef53ad6f 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -61,6 +61,7 @@ #include "xmpp/jid.h" #ifdef HAVE_OMEMO +#include "omemo/omemo.h" #include "xmpp/omemo.h" #endif @@ -318,7 +319,10 @@ session_login_success(gboolean secured) bookmark_request(); blocking_request(); #ifdef HAVE_OMEMO - omemo_devicelist_publish(); + omemo_devicelist_subscribe(); + if (omemo_loaded()) { + omemo_devicelist_publish(); + } #endif // items discovery diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index ffd0c053..e6400b0b 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2093,7 +2093,7 @@ stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, } xmpp_stanza_t* -stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char *const jid) +stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid) { char *id = connection_create_stanza_id("omemo_devicelist_subscribe"); xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); @@ -2104,7 +2104,7 @@ stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char * xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); xmpp_stanza_t *subscribe = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(subscribe, "subscribe"); + xmpp_stanza_set_name(subscribe, STANZA_NAME_SUBSCRIBE); xmpp_stanza_set_attribute(subscribe, "node", "eu.siacs.conversations.axolotl.devicelist"); xmpp_stanza_set_attribute(subscribe, "jid", jid); @@ -2117,6 +2117,149 @@ stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char * return iq; } +xmpp_stanza_t* +stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids) +{ + char *id = connection_create_stanza_id("omemo_devicelist_publish"); + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + free(id); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *publish = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); + xmpp_stanza_set_attribute(publish, "node", "eu.siacs.conversations.axolotl.devicelist"); + + xmpp_stanza_t *item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + xmpp_stanza_set_attribute(item, "id", "current"); + + xmpp_stanza_t *list = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(list, "list"); + xmpp_stanza_set_ns(list, "eu.siacs.conversations.axolotl"); + + GList *i; + for (i = ids; i != NULL; i = i->next) { + xmpp_stanza_t *device = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(device, "device"); + char *id = g_strdup_printf("%d", GPOINTER_TO_INT(i->data)); + xmpp_stanza_set_attribute(device, "id", id); + g_free(id); + + xmpp_stanza_add_child(list, device); + xmpp_stanza_release(device); + } + + xmpp_stanza_add_child(item, list); + xmpp_stanza_add_child(publish, item); + xmpp_stanza_add_child(pubsub, publish); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(list); + xmpp_stanza_release(item); + xmpp_stanza_release(publish); + xmpp_stanza_release(pubsub); + + return iq; +} + +xmpp_stanza_t* +stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, + const unsigned char * const identity_key, size_t identity_key_length, + const unsigned char * const signed_prekey, size_t signed_prekey_length, + const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, + GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length) +{ + char *id = connection_create_stanza_id("omemo_bundle_publish"); + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + free(id); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *publish = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); + char *node = g_strdup_printf("%s:%d", "eu.siacs.conversations.axolotl.bundles", device_id); + xmpp_stanza_set_attribute(publish, "node", node); + g_free(node); + + xmpp_stanza_t *item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + xmpp_stanza_set_attribute(item, "id", "current"); + + xmpp_stanza_t *bundle = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(bundle, "bundle"); + xmpp_stanza_set_ns(bundle, "eu.siacs.conversations.axolotl"); + + xmpp_stanza_t *signed_prekey_public_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(signed_prekey_public_stanza , "signedPreKeyPublic"); + xmpp_stanza_set_attribute(signed_prekey_public_stanza, "signedPreKeyId", "1"); + + xmpp_stanza_t *signed_prekey_public_stanza_text= xmpp_stanza_new(ctx); + xmpp_stanza_set_text(signed_prekey_public_stanza_text, g_base64_encode(signed_prekey, signed_prekey_length)); + xmpp_stanza_add_child(signed_prekey_public_stanza, signed_prekey_public_stanza_text); + xmpp_stanza_release(signed_prekey_public_stanza_text); + + xmpp_stanza_t *signed_prekey_signature_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(signed_prekey_signature_stanza , "signedPreKeySignature"); + + xmpp_stanza_t *signed_prekey_signature_stanza_text= xmpp_stanza_new(ctx); + xmpp_stanza_set_text(signed_prekey_signature_stanza_text, g_base64_encode(signed_prekey_signature, signed_prekey_signature_length)); + xmpp_stanza_add_child(signed_prekey_signature_stanza, signed_prekey_signature_stanza_text); + xmpp_stanza_release(signed_prekey_signature_stanza_text); + + xmpp_stanza_t *identity_key_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(identity_key_stanza , "identityKey"); + + xmpp_stanza_t *identity_key_stanza_text= xmpp_stanza_new(ctx); + xmpp_stanza_set_text(identity_key_stanza_text, g_base64_encode(identity_key, identity_key_length)); + xmpp_stanza_add_child(identity_key_stanza, identity_key_stanza_text); + xmpp_stanza_release(identity_key_stanza_text); + + xmpp_stanza_t *prekeys_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(prekeys_stanza, "prekeys"); + + GList *p, *i, *l; + for (p = prekeys, i = prekeys_id, l = prekeys_length; p != NULL; p = p->next, i = i->next, l = l->next) { + xmpp_stanza_t *prekey = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(prekey, "preKeyPublic"); + char *id = g_strdup_printf("%d", GPOINTER_TO_INT(i->data)); + xmpp_stanza_set_attribute(prekey, "id", id); + g_free(id); + + xmpp_stanza_t *prekey_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(prekey_text, g_base64_encode(p->data, GPOINTER_TO_INT(l->data))); + + xmpp_stanza_add_child(prekey, prekey_text); + xmpp_stanza_add_child(prekeys_stanza, prekey); + xmpp_stanza_release(prekey_text); + xmpp_stanza_release(prekey); + } + + xmpp_stanza_add_child(bundle, signed_prekey_public_stanza); + xmpp_stanza_add_child(bundle, signed_prekey_signature_stanza); + xmpp_stanza_add_child(bundle, identity_key_stanza); + xmpp_stanza_add_child(bundle, prekeys_stanza); + xmpp_stanza_add_child(item, bundle); + xmpp_stanza_add_child(publish, item); + xmpp_stanza_add_child(pubsub, publish); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(signed_prekey_public_stanza); + xmpp_stanza_release(signed_prekey_signature_stanza); + xmpp_stanza_release(identity_key_stanza); + xmpp_stanza_release(prekeys_stanza); + xmpp_stanza_release(bundle); + xmpp_stanza_release(item); + xmpp_stanza_release(publish); + xmpp_stanza_release(pubsub); + + return iq; +} + static void _stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index ce655a93..9cca027d 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -82,6 +82,7 @@ #define STANZA_NAME_PUBSUB "pubsub" #define STANZA_NAME_PUBLISH "publish" #define STANZA_NAME_PUBLISH_OPTIONS "publish-options" +#define STANZA_NAME_SUBSCRIBE "subscribe" #define STANZA_NAME_FIELD "field" #define STANZA_NAME_STORAGE "storage" #define STANZA_NAME_NICK "nick" @@ -284,7 +285,9 @@ xmpp_stanza_t* stanza_create_room_kick_iq(xmpp_ctx_t *const ctx, const char *con xmpp_stanza_t* stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node); xmpp_stanza_t* stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, const char *const node, const char *const sessionid, DataForm *form); -xmpp_stanza_t* stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char *const jid); +xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid); +xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids); +xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length); int stanza_get_idle_time(xmpp_stanza_t *const stanza); -- cgit 1.4.1-2-gfad0 From 2d28725c8573c0d7b906f15ff334a8154831661f Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Feb 2019 10:40:41 +0100 Subject: Rename ProfIdCallback into ProfIqCallback Goal is to create other kind of callback no based on id cmp --- src/xmpp/iq.c | 24 ++++++++++++------------ src/xmpp/iq.h | 6 +++--- src/xmpp/roster.c | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index a77ef59b..e1c3c281 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -77,11 +77,11 @@ typedef struct p_room_info_data_t { gboolean display; } ProfRoomInfoData; -typedef struct p_id_handle_t { - ProfIdCallback func; - ProfIdFreeCallback free_func; +typedef struct p_iq_handle_t { + ProfIqCallback func; + ProfIqFreeCallback free_func; void *userdata; -} ProfIdHandler; +} ProfIqHandler; typedef struct privilege_set_t { char *item; @@ -205,7 +205,7 @@ _iq_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const us const char *id = xmpp_stanza_get_id(stanza); if (id) { - ProfIdHandler *handler = g_hash_table_lookup(id_handlers, id); + ProfIqHandler *handler = g_hash_table_lookup(id_handlers, id); if (handler) { int keep = handler->func(stanza, handler->userdata); if (!keep) { @@ -234,7 +234,7 @@ iq_handlers_init(void) GList *keys = g_hash_table_get_keys(id_handlers); GList *curr = keys; while (curr) { - ProfIdHandler *handler = g_hash_table_lookup(id_handlers, curr->data); + ProfIqHandler *handler = g_hash_table_lookup(id_handlers, curr->data); if (handler->free_func && handler->userdata) { handler->free_func(handler->userdata); } @@ -248,9 +248,9 @@ iq_handlers_init(void) } void -iq_id_handler_add(const char *const id, ProfIdCallback func, ProfIdFreeCallback free_func, void *userdata) +iq_id_handler_add(const char *const id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata) { - ProfIdHandler *handler = malloc(sizeof(ProfIdHandler)); + ProfIqHandler *handler = malloc(sizeof(ProfIqHandler)); handler->func = func; handler->free_func = free_func; handler->userdata = userdata; @@ -438,7 +438,7 @@ iq_room_info_request(const char *const room, gboolean display_result) cb_data->room = strdup(room); cb_data->display = display_result; - iq_id_handler_add(id, _room_info_response_id_handler, (ProfIdFreeCallback)_iq_free_room_data, cb_data); + iq_id_handler_add(id, _room_info_response_id_handler, (ProfIqFreeCallback)_iq_free_room_data, cb_data); free(id); @@ -651,7 +651,7 @@ iq_room_affiliation_set(const char *const room, const char *const jid, char *aff affiliation_set->item = strdup(jid); affiliation_set->privilege = strdup(affiliation); - iq_id_handler_add(id, _room_affiliation_set_result_id_handler, (ProfIdFreeCallback)_iq_free_affiliation_set, affiliation_set); + iq_id_handler_add(id, _room_affiliation_set_result_id_handler, (ProfIqFreeCallback)_iq_free_affiliation_set, affiliation_set); iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -670,7 +670,7 @@ iq_room_role_set(const char *const room, const char *const nick, char *role, role_set->item = strdup(nick); role_set->privilege = strdup(role); - iq_id_handler_add(id, _room_role_set_result_id_handler, (ProfIdFreeCallback)_iq_free_affiliation_set, role_set); + iq_id_handler_add(id, _room_role_set_result_id_handler, (ProfIqFreeCallback)_iq_free_affiliation_set, role_set); iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -697,7 +697,7 @@ iq_send_ping(const char *const target) const char *id = xmpp_stanza_get_id(iq); GDateTime *now = g_date_time_new_now_local(); - iq_id_handler_add(id, _manual_pong_id_handler, (ProfIdFreeCallback)g_date_time_unref, now); + iq_id_handler_add(id, _manual_pong_id_handler, (ProfIqFreeCallback)g_date_time_unref, now); iq_send_stanza(iq); xmpp_stanza_release(iq); diff --git a/src/xmpp/iq.h b/src/xmpp/iq.h index 025d5e9f..bc273db4 100644 --- a/src/xmpp/iq.h +++ b/src/xmpp/iq.h @@ -35,12 +35,12 @@ #ifndef XMPP_IQ_H #define XMPP_IQ_H -typedef int(*ProfIdCallback)(xmpp_stanza_t *const stanza, void *const userdata); -typedef void(*ProfIdFreeCallback)(void *userdata); +typedef int(*ProfIqCallback)(xmpp_stanza_t *const stanza, void *const userdata); +typedef void(*ProfIqFreeCallback)(void *userdata); void iq_handlers_init(void); void iq_send_stanza(xmpp_stanza_t *const stanza); -void iq_id_handler_add(const char *const id, ProfIdCallback func, ProfIdFreeCallback free_func, void *userdata); +void iq_id_handler_add(const char *const id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata); void iq_disco_info_request_onconnect(gchar *jid); void iq_disco_items_request_onconnect(gchar *jid); void iq_send_caps_request(const char *const to, const char *const id, const char *const node, const char *const ver); diff --git a/src/xmpp/roster.c b/src/xmpp/roster.c index 9be154e7..fe15515f 100644 --- a/src/xmpp/roster.c +++ b/src/xmpp/roster.c @@ -137,7 +137,7 @@ roster_send_add_to_group(const char *const group, PContact contact) } xmpp_ctx_t * const ctx = connection_get_ctx(); - iq_id_handler_add(unique_id, _group_add_id_handler, (ProfIdFreeCallback)_free_group_data, data); + iq_id_handler_add(unique_id, _group_add_id_handler, (ProfIqFreeCallback)_free_group_data, data); xmpp_stanza_t *iq = stanza_create_roster_set(ctx, unique_id, p_contact_barejid(contact), p_contact_name(contact), new_groups); iq_send_stanza(iq); @@ -180,7 +180,7 @@ roster_send_remove_from_group(const char *const group, PContact contact) data->name = strdup(p_contact_barejid(contact)); } - iq_id_handler_add(unique_id, _group_remove_id_handler, (ProfIdFreeCallback)_free_group_data, data); + iq_id_handler_add(unique_id, _group_remove_id_handler, (ProfIqFreeCallback)_free_group_data, data); xmpp_stanza_t *iq = stanza_create_roster_set(ctx, unique_id, p_contact_barejid(contact), p_contact_name(contact), new_groups); iq_send_stanza(iq); -- cgit 1.4.1-2-gfad0 From 1ae5d9f1f8a0d2005feaf797113d44bbe294c032 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Feb 2019 11:16:42 +0100 Subject: Add pubsub event callback Also add generic message callback struct --- src/xmpp/message.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmpp/message.h | 4 ++++ src/xmpp/stanza.h | 1 + 3 files changed, 65 insertions(+) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index adea5c10..8240402d 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -62,6 +62,12 @@ #include "xmpp/connection.h" #include "xmpp/xmpp.h" +typedef struct p_message_handle_t { + ProfMessageCallback func; + ProfMessageFreeCallback free_func; + void *userdata; +} ProfMessageHandler; + static int _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata); static void _handle_error(xmpp_stanza_t *const stanza); @@ -70,10 +76,13 @@ static void _handel_muc_user(xmpp_stanza_t *const stanza); static void _handle_conference(xmpp_stanza_t *const stanza); static void _handle_captcha(xmpp_stanza_t *const stanza); static void _handle_receipt_received(xmpp_stanza_t *const stanza); +static void _handle_pubsub_event(xmpp_stanza_t *const stanza); static void _handle_chat(xmpp_stanza_t *const stanza); static void _send_message_stanza(xmpp_stanza_t *const stanza); +static GHashTable *pubsub_event_handlers; + static int _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { @@ -118,6 +127,11 @@ _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *con _handle_receipt_received(stanza); } + xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); + if (event) { + _handle_pubsub_event(event); + } + _handle_chat(stanza); return 1; @@ -129,6 +143,33 @@ message_handlers_init(void) xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_handler_add(conn, _message_handler, NULL, STANZA_NAME_MESSAGE, NULL, ctx); + + if (pubsub_event_handlers) { + GList *keys = g_hash_table_get_keys(pubsub_event_handlers); + GList *curr = keys; + while (curr) { + ProfMessageHandler *handler = g_hash_table_lookup(pubsub_event_handlers, curr->data); + if (handler->free_func && handler->userdata) { + handler->free_func(handler->userdata); + } + curr = g_list_next(curr); + } + g_list_free(keys); + g_hash_table_destroy(pubsub_event_handlers); + } + + pubsub_event_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); +} + +void +message_pubsub_event_handler_add(const char *const node, ProfMessageCallback func, ProfMessageFreeCallback free_func, void *userdata) +{ + ProfMessageHandler *handler = malloc(sizeof(ProfMessageHandler)); + handler->func = func; + handler->free_func = free_func; + handler->userdata = userdata; + + g_hash_table_insert(pubsub_event_handlers, strdup(node), handler); } char* @@ -624,6 +665,25 @@ _handle_receipt_received(xmpp_stanza_t *const stanza) jid_destroy(jidp); } +static void +_handle_pubsub_event(xmpp_stanza_t *const event) +{ + xmpp_stanza_t *child = xmpp_stanza_get_children(event); + if (child) { + const char *node = xmpp_stanza_get_attribute(event, STANZA_ATTR_NODE); + if (node) { + ProfMessageHandler *handler = g_hash_table_lookup(pubsub_event_handlers, node); + if (handler) { + int keep = handler->func(event, handler->userdata); + if (!keep) { + free(handler); + g_hash_table_remove(pubsub_event_handlers, node); + } + } + } + } +} + void _receipt_request_handler(xmpp_stanza_t *const stanza) { diff --git a/src/xmpp/message.h b/src/xmpp/message.h index dee9be2d..0c81ca39 100644 --- a/src/xmpp/message.h +++ b/src/xmpp/message.h @@ -35,6 +35,10 @@ #ifndef XMPP_MESSAGE_H #define XMPP_MESSAGE_H +typedef int(*ProfMessageCallback)(xmpp_stanza_t *const stanza, void *const userdata); +typedef void(*ProfMessageFreeCallback)(void *userdata); + void message_handlers_init(void); +void message_pubsub_event_handler_add(const char *const node, ProfMessageCallback func, ProfMessageFreeCallback free_func, void *userdata); #endif diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 9cca027d..df9c27ff 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -180,6 +180,7 @@ #define STANZA_NS_CONFERENCE "jabber:x:conference" #define STANZA_NS_CAPTCHA "urn:xmpp:captcha" #define STANZA_NS_PUBSUB "http://jabber.org/protocol/pubsub" +#define STANZA_NS_PUBSUB_EVENT "http://jabber.org/protocol/pubsub#event" #define STANZA_NS_CARBONS "urn:xmpp:carbons:2" #define STANZA_NS_HINTS "urn:xmpp:hints" #define STANZA_NS_FORWARD "urn:xmpp:forward:0" -- cgit 1.4.1-2-gfad0 From d8d7a5348c4f444f4452ebaa42a103b63dfc739d Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Feb 2019 19:56:12 +0140 Subject: Ensure OMEMO isn't started before starting OTR --- src/command/cmd_funcs.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 08aa88df..ab5243c1 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7308,6 +7308,11 @@ cmd_otr_start(ProfWin *window, const char *const command, gchar **args) return TRUE; } + if (chatwin->is_omemo) { + win_println(window, THEME_DEFAULT, '!', "You must disable OMEMO before starting an OTR session."); + return TRUE; + } + if (chatwin->is_otr) { win_println(window, THEME_DEFAULT, '!', "You are already in an OTR session."); return TRUE; -- cgit 1.4.1-2-gfad0 From 79bb5016c22ee663f94b508feb59cb1d302b00b0 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Feb 2019 19:56:26 +0140 Subject: Ensure not to generate omemo crypto twice --- src/command/cmd_funcs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index ab5243c1..ab438c7f 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7887,7 +7887,12 @@ cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args) { #ifdef HAVE_OMEMO if (connection_get_status() != JABBER_CONNECTED) { - cons_show("You must be connected with an account to initialize OMEMO"); + cons_show("You must be connected with an account to initialize OMEMO."); + return TRUE; + } + + if (omemo_loaded()) { + cons_show("OMEMO crytographic materials have already been generated."); return TRUE; } -- cgit 1.4.1-2-gfad0 From 756fefb09aa9cb22d1f882c51984ca6047dc236d Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Feb 2019 19:57:26 +0140 Subject: Fix devicelist subscription and handle pubsub event Devicelist subscription can be done directly with caps_add feature. --- src/omemo/omemo.c | 32 +++++++++++++++++--------- src/omemo/omemo.h | 4 +++- src/xmpp/message.c | 35 ++++++++++++----------------- src/xmpp/omemo.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++------- src/xmpp/omemo.h | 4 +++- src/xmpp/session.c | 2 +- src/xmpp/stanza.c | 4 ++-- src/xmpp/stanza.h | 2 ++ src/xmpp/xmpp.h | 1 + 9 files changed, 106 insertions(+), 44 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 080baf1b..9300c6b8 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -11,6 +11,8 @@ #include "omemo/crypto.h" #include "omemo/omemo.h" #include "ui/ui.h" +#include "xmpp/xmpp.h" +#include "xmpp/connection.h" #include "xmpp/omemo.h" static gboolean loaded; @@ -23,7 +25,7 @@ struct omemo_context_t { pthread_mutex_t lock; signal_context *signal; uint32_t device_id; - GList *device_list; + GHashTable *device_list; ratchet_identity_key_pair *identity_key_pair; uint32_t registration_id; signal_protocol_key_helper_pre_key_list_node *pre_keys_head; @@ -72,14 +74,23 @@ omemo_init(void) signal_context_set_locking_functions(omemo_ctx.signal, lock, unlock); loaded = FALSE; - omemo_ctx.device_list = NULL; + omemo_ctx.device_list = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_list_free); } void omemo_generate_crypto_materials(ProfAccount *account) { + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + + GList *device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid); + g_hash_table_steal(omemo_ctx.device_list, barejid); + omemo_ctx.device_id = randombytes_uniform(0x80000000); - omemo_ctx.device_list = g_list_append(omemo_ctx.device_list, GINT_TO_POINTER(omemo_ctx.device_id)); + + device_list = g_list_append(device_list, GINT_TO_POINTER(omemo_ctx.device_id)); + g_hash_table_insert(omemo_ctx.device_list, strdup(barejid), device_list); + signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); signal_protocol_key_helper_generate_pre_keys(&omemo_ctx.pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); @@ -91,7 +102,7 @@ omemo_generate_crypto_materials(ProfAccount *account) loaded = TRUE; - omemo_devicelist_publish(); + omemo_devicelist_publish(device_list); omemo_bundle_publish(); } @@ -107,12 +118,6 @@ omemo_loaded(void) return loaded; } -GList * const -omemo_device_list(void) -{ - return omemo_ctx.device_list; -} - uint32_t omemo_device_id(void) { @@ -167,6 +172,13 @@ omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths } } +void +omemo_set_device_list(const char *const jid, GList * const device_list) +{ + /* TODO handle self device_list to ensure we still are on the list */ + g_hash_table_insert(omemo_ctx.device_list, strdup(jid), device_list); +} + static void lock(void *user_data) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 20fd5d5d..ca9f7208 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -1,3 +1,5 @@ +#include + #include "config/account.h" typedef struct omemo_context_t omemo_context; @@ -5,12 +7,12 @@ typedef struct omemo_context_t omemo_context; void omemo_init(void); void omemo_generate_crypto_materials(ProfAccount *account); -GList * const omemo_device_list(void); uint32_t omemo_device_id(void); void omemo_identity_key(unsigned char **output, size_t *length); void omemo_signed_prekey(unsigned char **output, size_t *length); void omemo_signed_prekey_signature(unsigned char **output, size_t *length); void omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths); +void omemo_set_device_list(const char *const jid, GList * const device_list); void omemo_start_session(ProfAccount *account, char *barejid); gboolean omemo_loaded(void); diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 8240402d..fba62bc8 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -76,7 +76,6 @@ static void _handel_muc_user(xmpp_stanza_t *const stanza); static void _handle_conference(xmpp_stanza_t *const stanza); static void _handle_captcha(xmpp_stanza_t *const stanza); static void _handle_receipt_received(xmpp_stanza_t *const stanza); -static void _handle_pubsub_event(xmpp_stanza_t *const stanza); static void _handle_chat(xmpp_stanza_t *const stanza); static void _send_message_stanza(xmpp_stanza_t *const stanza); @@ -129,7 +128,20 @@ _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *con xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); if (event) { - _handle_pubsub_event(event); + xmpp_stanza_t *child = xmpp_stanza_get_children(event); + if (child) { + const char *node = xmpp_stanza_get_attribute(child, STANZA_ATTR_NODE); + if (node) { + ProfMessageHandler *handler = g_hash_table_lookup(pubsub_event_handlers, node); + if (handler) { + int keep = handler->func(stanza, handler->userdata); + if (!keep) { + free(handler); + g_hash_table_remove(pubsub_event_handlers, node); + } + } + } + } } _handle_chat(stanza); @@ -665,25 +677,6 @@ _handle_receipt_received(xmpp_stanza_t *const stanza) jid_destroy(jidp); } -static void -_handle_pubsub_event(xmpp_stanza_t *const event) -{ - xmpp_stanza_t *child = xmpp_stanza_get_children(event); - if (child) { - const char *node = xmpp_stanza_get_attribute(event, STANZA_ATTR_NODE); - if (node) { - ProfMessageHandler *handler = g_hash_table_lookup(pubsub_event_handlers, node); - if (handler) { - int keep = handler->func(event, handler->userdata); - if (!keep) { - free(handler); - g_hash_table_remove(pubsub_event_handlers, node); - } - } - } - } -} - void _receipt_request_handler(xmpp_stanza_t *const stanza) { diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index a979ec90..ab10e975 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -1,30 +1,36 @@ +#include + #include "xmpp/connection.h" +#include "xmpp/message.h" #include "xmpp/iq.h" #include "xmpp/stanza.h" #include "omemo/omemo.h" +static int _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata); + void omemo_devicelist_subscribe(void) { - xmpp_ctx_t * const ctx = connection_get_ctx(); - char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); - xmpp_stanza_t *iq = stanza_create_omemo_devicelist_subscribe(ctx, barejid); - iq_send_stanza(iq); - xmpp_stanza_release(iq); + message_pubsub_event_handler_add(STANZA_NS_OMEMO_DEVICELIST, _omemo_receive_devicelist, NULL, NULL); - free(barejid); + caps_add_feature(XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY); } void -omemo_devicelist_publish(void) +omemo_devicelist_publish(GList *device_list) { xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, omemo_device_list()); + xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, device_list); iq_send_stanza(iq); xmpp_stanza_release(iq); } +void +omemo_devicelist_fetch(void) +{ +} + void omemo_bundle_publish(void) { @@ -53,3 +59,47 @@ omemo_bundle_publish(void) free(signed_prekey); free(signed_prekey_signature); } + +void +omemo_bundles_fetch(const char * const jid) +{ +} + +static int +_omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) +{ + GList *device_list = NULL; + const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + if (!from) { + return 1; + } + + xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); + if (!event) { + return 1; + } + + xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(event, "items"); + if (!items) { + return 1; + } + + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); + if (!item) { + return 1; + } + + xmpp_stanza_t *list = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO); + if (!list) { + return 1; + } + + xmpp_stanza_t *device; + for (device = xmpp_stanza_get_children(list); device != NULL; device = xmpp_stanza_get_next(device)) { + const char *id = xmpp_stanza_get_id(device); + device_list = g_list_append(device_list, GINT_TO_POINTER(strtoul(id, NULL, 10))); + } + omemo_set_device_list(from, device_list); + + return 1; +} diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index eff3eee3..5409c6fd 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -1,3 +1,5 @@ +#include + void omemo_devicelist_subscribe(void); -void omemo_devicelist_publish(void); +void omemo_devicelist_publish(GList *device_list); void omemo_bundle_publish(void); diff --git a/src/xmpp/session.c b/src/xmpp/session.c index ef53ad6f..b05c5f7b 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -321,7 +321,7 @@ session_login_success(gboolean secured) #ifdef HAVE_OMEMO omemo_devicelist_subscribe(); if (omemo_loaded()) { - omemo_devicelist_publish(); + /* TODO: update devicelist */ } #endif diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index e6400b0b..e2ced2ac 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2105,7 +2105,7 @@ stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid) xmpp_stanza_t *subscribe = xmpp_stanza_new(ctx); xmpp_stanza_set_name(subscribe, STANZA_NAME_SUBSCRIBE); - xmpp_stanza_set_attribute(subscribe, "node", "eu.siacs.conversations.axolotl.devicelist"); + xmpp_stanza_set_attribute(subscribe, "node", STANZA_NS_OMEMO_DEVICELIST); xmpp_stanza_set_attribute(subscribe, "jid", jid); xmpp_stanza_add_child(pubsub, subscribe); @@ -2130,7 +2130,7 @@ stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids) xmpp_stanza_t *publish = xmpp_stanza_new(ctx); xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); - xmpp_stanza_set_attribute(publish, "node", "eu.siacs.conversations.axolotl.devicelist"); + xmpp_stanza_set_attribute(publish, "node", STANZA_NS_OMEMO_DEVICELIST); xmpp_stanza_t *item = xmpp_stanza_new(ctx); xmpp_stanza_set_name(item, STANZA_NAME_ITEM); diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index df9c27ff..23b47de1 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -191,6 +191,8 @@ #define STANZA_NS_X_OOB "jabber:x:oob" #define STANZA_NS_BLOCKING "urn:xmpp:blocking" #define STANZA_NS_COMMAND "http://jabber.org/protocol/commands" +#define STANZA_NS_OMEMO "eu.siacs.conversations.axolotl" +#define STANZA_NS_OMEMO_DEVICELIST "eu.siacs.conversations.axolotl.devicelist" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index c9403090..5c0dae76 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -61,6 +61,7 @@ #define XMPP_FEATURE_LASTACTIVITY "jabber:iq:last" #define XMPP_FEATURE_MUC "http://jabber.org/protocol/muc" #define XMPP_FEATURE_COMMANDS "http://jabber.org/protocol/commands" +#define XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY "eu.siacs.conversations.axolotl.devicelist+notify" typedef enum { JABBER_CONNECTING, -- cgit 1.4.1-2-gfad0 From 95da03ac28c283e59588bebf698b12aea2333c1f Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Feb 2019 06:04:03 +0140 Subject: Ensure received devicelist contains our own device id --- src/omemo/omemo.c | 13 +++++++++++-- src/omemo/omemo.h | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 9300c6b8..3ab044ee 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -173,9 +173,18 @@ omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths } void -omemo_set_device_list(const char *const jid, GList * const device_list) +omemo_set_device_list(const char *const jid, GList * device_list) { - /* TODO handle self device_list to ensure we still are on the list */ + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + + if (g_strcmp0(jid, barejid) == 0) { + if (!g_list_find(device_list, GINT_TO_POINTER(omemo_ctx.device_id))) { + device_list = g_list_append(device_list, GINT_TO_POINTER(omemo_ctx.device_id)); + omemo_devicelist_publish(device_list); + } + } + g_hash_table_insert(omemo_ctx.device_list, strdup(jid), device_list); } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index ca9f7208..31d942b8 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -12,7 +12,7 @@ void omemo_identity_key(unsigned char **output, size_t *length); void omemo_signed_prekey(unsigned char **output, size_t *length); void omemo_signed_prekey_signature(unsigned char **output, size_t *length); void omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths); -void omemo_set_device_list(const char *const jid, GList * const device_list); +void omemo_set_device_list(const char *const jid, GList * device_list); void omemo_start_session(ProfAccount *account, char *barejid); gboolean omemo_loaded(void); -- cgit 1.4.1-2-gfad0 From 1f9d46037cb86b26a7e91d924f41702a6ea58114 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Feb 2019 06:39:41 +0140 Subject: Request for device list when OMEMO is ready --- src/omemo/omemo.c | 10 +++------- src/xmpp/omemo.c | 26 +++++++++++++++++++++++--- src/xmpp/omemo.h | 1 + src/xmpp/stanza.c | 24 ++++++++++++++++++++++++ src/xmpp/stanza.h | 1 + 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 3ab044ee..e4926baa 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -83,14 +83,8 @@ omemo_generate_crypto_materials(ProfAccount *account) xmpp_ctx_t * const ctx = connection_get_ctx(); char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); - GList *device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid); - g_hash_table_steal(omemo_ctx.device_list, barejid); - omemo_ctx.device_id = randombytes_uniform(0x80000000); - device_list = g_list_append(device_list, GINT_TO_POINTER(omemo_ctx.device_id)); - g_hash_table_insert(omemo_ctx.device_list, strdup(barejid), device_list); - signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); signal_protocol_key_helper_generate_pre_keys(&omemo_ctx.pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); @@ -102,7 +96,9 @@ omemo_generate_crypto_materials(ProfAccount *account) loaded = TRUE; - omemo_devicelist_publish(device_list); + /* Ensure we get our current device list, and it gets updated with our + * device_id */ + omemo_devicelist_request(barejid); omemo_bundle_publish(); } diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index ab10e975..5418b1b4 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -27,8 +27,18 @@ omemo_devicelist_publish(GList *device_list) } void -omemo_devicelist_fetch(void) +omemo_devicelist_request(const char * const jid) { + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *id = connection_create_stanza_id("devicelist_request"); + + xmpp_stanza_t *iq = stanza_create_omemo_devicelist_request(ctx, id, jid); + iq_id_handler_add(id, _omemo_receive_devicelist, NULL, NULL); + + iq_send_stanza(iq); + + free(id); + xmpp_stanza_release(iq); } void @@ -74,12 +84,22 @@ _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) return 1; } + xmpp_stanza_t *root = NULL; xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); - if (!event) { + if (event) { + root = event; + } + + xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB); + if (pubsub) { + root = pubsub; + } + + if (!root) { return 1; } - xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(event, "items"); + xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(root, "items"); if (!items) { return 1; } diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index 5409c6fd..b125f76a 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -2,4 +2,5 @@ void omemo_devicelist_subscribe(void); void omemo_devicelist_publish(GList *device_list); +void omemo_devicelist_request(const char * const jid); void omemo_bundle_publish(void); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index e2ced2ac..d5993377 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2092,6 +2092,30 @@ stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, return iq; } +xmpp_stanza_t* +stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const char *const id, + const char *const jid) +{ + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id); + xmpp_stanza_set_to(iq, jid); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *items = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(items, "items"); + xmpp_stanza_set_attribute(items, "node", STANZA_NS_OMEMO_DEVICELIST); + + xmpp_stanza_add_child(pubsub, items); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(items); + xmpp_stanza_release(pubsub); + + return iq; +} + xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 23b47de1..919379bb 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -288,6 +288,7 @@ xmpp_stanza_t* stanza_create_room_kick_iq(xmpp_ctx_t *const ctx, const char *con xmpp_stanza_t* stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node); xmpp_stanza_t* stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, const char *const node, const char *const sessionid, DataForm *form); +xmpp_stanza_t* stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids); xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length); -- cgit 1.4.1-2-gfad0 From f9216fddb106d46bac2d13d9dfe175b4a475a789 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Feb 2019 18:07:11 +0140 Subject: Add signal store backend and OMEMO start command --- Makefile.am | 3 +- src/command/cmd_defs.c | 3 +- src/command/cmd_funcs.c | 53 ++++++++++ src/command/cmd_funcs.h | 1 + src/omemo/omemo.c | 80 ++++++++++++++- src/omemo/omemo.h | 6 +- src/omemo/store.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++ src/omemo/store.h | 34 ++++++ src/xmpp/omemo.c | 74 +++++++++++++- src/xmpp/omemo.h | 4 + src/xmpp/stanza.c | 25 +++++ src/xmpp/stanza.h | 2 + 12 files changed, 545 insertions(+), 7 deletions(-) create mode 100644 src/omemo/store.c create mode 100644 src/omemo/store.h diff --git a/Makefile.am b/Makefile.am index d5e92bce..ca60ac95 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,6 +71,7 @@ unittest_sources = \ src/pgp/gpg.h \ src/omemo/omemo.h \ src/omemo/crypto.h \ + src/omemo/store.h \ src/command/cmd_defs.h src/command/cmd_defs.c \ src/command/cmd_funcs.h src/command/cmd_funcs.c \ src/command/cmd_ac.h src/command/cmd_ac.c \ @@ -172,7 +173,7 @@ otr4_sources = \ omemo_sources = \ src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c \ - src/xmpp/omemo.h src/xmpp/omemo.c + src/omemo/store.h src/omemo/store.c src/xmpp/omemo.h src/xmpp/omemo.c if BUILD_PYTHON_API core_sources += $(python_sources) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index af30db34..265af119 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2333,7 +2333,8 @@ static struct cmd_t command_defs[] = { "/omemo", parse_args, 1, 3, NULL, CMD_SUBFUNCS( - { "gen", cmd_omemo_gen }) + { "gen", cmd_omemo_gen }, + { "start", cmd_omemo_start }) CMD_NOMAINFUNC CMD_TAGS( CMD_TAG_CHAT, diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index ab438c7f..66715d20 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7904,3 +7904,56 @@ cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args) return TRUE; #endif } + +gboolean +cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OMEMO information."); + return TRUE; + } + + // recipient supplied + if (args[1]) { + char *contact = args[1]; + char *barejid = roster_barejid_from_name(contact); + if (barejid == NULL) { + barejid = contact; + } + + ProfChatWin *chatwin = wins_get_chat(barejid); + if (!chatwin) { + chatwin = chatwin_new(barejid); + } + ui_focus_win((ProfWin*)chatwin); + + if (chatwin->pgp_send) { + win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session."); + return TRUE; + } + + if (chatwin->is_otr) { + win_println(window, THEME_DEFAULT, '!', "You must disable OTR encryption before starting an OMEMO session."); + return TRUE; + } + + if (chatwin->is_omemo) { + win_println(window, THEME_DEFAULT, '!', "You are already in an OMEMO session."); + return TRUE; + } + + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo init'"); + return TRUE; + } + + omemo_start_session(barejid); + } + + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 650c9eaf..f39e8c17 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -215,5 +215,6 @@ gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args) gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index e4926baa..32fdce42 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -4,10 +4,12 @@ #include #include #include +#include #include #include "config/account.h" #include "log.h" +#include "omemo/store.h" #include "omemo/crypto.h" #include "omemo/omemo.h" #include "ui/ui.h" @@ -30,6 +32,11 @@ struct omemo_context_t { uint32_t registration_id; signal_protocol_key_helper_pre_key_list_node *pre_keys_head; session_signed_pre_key *signed_pre_key; + signal_protocol_store_context *store; + GHashTable *session_store; + GHashTable *pre_key_store; + GHashTable *signed_pre_key_store; + identity_key_store_t identity_key_store; }; static omemo_context omemo_ctx; @@ -73,6 +80,53 @@ omemo_init(void) signal_context_set_locking_functions(omemo_ctx.signal, lock, unlock); + signal_protocol_store_context_create(&omemo_ctx.store, omemo_ctx.signal); + + omemo_ctx.session_store = session_store_new(); + signal_protocol_session_store session_store = { + .load_session_func = load_session, + .get_sub_device_sessions_func = get_sub_device_sessions, + .store_session_func = store_session, + .contains_session_func = contains_session, + .delete_session_func = delete_session, + .delete_all_sessions_func = delete_all_sessions, + .destroy_func = NULL, + .user_data = omemo_ctx.session_store + }; + signal_protocol_store_context_set_session_store(omemo_ctx.store, &session_store); + + omemo_ctx.pre_key_store = pre_key_store_new(); + signal_protocol_pre_key_store pre_key_store = { + .load_pre_key = load_pre_key, + .store_pre_key = store_pre_key, + .contains_pre_key = contains_pre_key, + .remove_pre_key = remove_pre_key, + .destroy_func = NULL, + .user_data = omemo_ctx.pre_key_store + }; + signal_protocol_store_context_set_pre_key_store(omemo_ctx.store, &pre_key_store); + + omemo_ctx.signed_pre_key_store = signed_pre_key_store_new(); + signal_protocol_signed_pre_key_store signed_pre_key_store = { + .load_signed_pre_key = load_signed_pre_key, + .store_signed_pre_key = store_signed_pre_key, + .contains_signed_pre_key = contains_signed_pre_key, + .remove_signed_pre_key = remove_signed_pre_key, + .destroy_func = NULL, + .user_data = omemo_ctx.pre_key_store + }; + signal_protocol_store_context_set_signed_pre_key_store(omemo_ctx.store, &signed_pre_key_store); + + identity_key_store_new(&omemo_ctx.identity_key_store); + signal_protocol_identity_key_store identity_key_store = { + .get_identity_key_pair = get_identity_key_pair, + .get_local_registration_id = get_local_registration_id, + .save_identity = save_identity, + .is_trusted_identity = is_trusted_identity, + }; + signal_protocol_store_context_set_identity_key_store(omemo_ctx.store, &identity_key_store); + + loaded = FALSE; omemo_ctx.device_list = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_list_free); } @@ -103,9 +157,19 @@ omemo_generate_crypto_materials(ProfAccount *account) } void -omemo_start_session(ProfAccount *account, char *barejid) +omemo_start_session(const char *const barejid) { + GList *device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid); + if (!device_list) { + omemo_devicelist_request(barejid); + /* TODO handle response */ + return; + } + GList *device_id; + for (device_id = device_list; device_id != NULL; device_id = device_id->next) { + omemo_bundle_request(barejid, GPOINTER_TO_INT(device_id->data), omemo_start_device_session_handle_bundle, free, strdup(barejid)); + } } gboolean @@ -151,7 +215,7 @@ omemo_signed_prekey_signature(unsigned char **output, size_t *length) } void -omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths) +omemo_prekeys(GList **prekeys, GList **ids, GList **lengths) { signal_protocol_key_helper_pre_key_list_node *p; for (p = omemo_ctx.pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { @@ -184,6 +248,18 @@ omemo_set_device_list(const char *const jid, GList * device_list) g_hash_table_insert(omemo_ctx.device_list, strdup(jid), device_list); } +void +omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const prekey, size_t prekey_len) +{ + signal_protocol_address address = { + jid, strlen(jid), device_id + }; + + session_builder *builder; + session_builder_create(&builder, omemo_ctx.store, &address, omemo_ctx.signal); + //session_builder_process_pre_key_bundle(builder, prekey); +} + static void lock(void *user_data) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 31d942b8..26bd6e65 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -11,8 +11,10 @@ uint32_t omemo_device_id(void); void omemo_identity_key(unsigned char **output, size_t *length); void omemo_signed_prekey(unsigned char **output, size_t *length); void omemo_signed_prekey_signature(unsigned char **output, size_t *length); -void omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths); +void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); -void omemo_start_session(ProfAccount *account, char *barejid); +void omemo_start_session(const char *const barejid); +void omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const prekey, size_t prekey_len); + gboolean omemo_loaded(void); diff --git a/src/omemo/store.c b/src/omemo/store.c new file mode 100644 index 00000000..384eb9ce --- /dev/null +++ b/src/omemo/store.c @@ -0,0 +1,267 @@ +#include +#include + +#include "omemo/store.h" + +GHashTable * +session_store_new(void) +{ + return g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); +} + +GHashTable * +pre_key_store_new(void) +{ + return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); +} + +GHashTable * +signed_pre_key_store_new(void) +{ + return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); +} + +void +identity_key_store_new(identity_key_store_t *identity_key_store) +{ + identity_key_store->identity_key_store = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)signal_buffer_free); + identity_key_store->private = NULL; + identity_key_store->public = NULL; +} + +int +load_session(signal_buffer **record, const signal_protocol_address *address, + void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, address->name); + if (!device_store) { + *record = NULL; + return SG_SUCCESS; + } + + signal_buffer *original = g_hash_table_lookup(device_store, GINT_TO_POINTER(address->device_id)); + *record = signal_buffer_copy(original); + return SG_SUCCESS; +} + +int +get_sub_device_sessions(signal_int_list **sessions, const char *name, + size_t name_len, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + GHashTableIter iter; + gpointer key, value; + + device_store = g_hash_table_lookup(session_store, name); + if (!device_store) { + return SG_SUCCESS; + } + + *sessions = signal_int_list_alloc(); + g_hash_table_iter_init(&iter, device_store); + while (g_hash_table_iter_next(&iter, &key, &value)) { + signal_int_list_push_back(*sessions, GPOINTER_TO_INT(key)); + } + + + return SG_SUCCESS; +} + +int +store_session(const signal_protocol_address *address, uint8_t *record, + size_t record_len, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, (void *)address->name); + if (!device_store) { + device_store = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); + g_hash_table_insert(session_store, strdup(address->name), device_store); + } + + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(device_store, GINT_TO_POINTER(address->device_id), buffer); + return SG_SUCCESS; +} + +int +contains_session(const signal_protocol_address *address, void *user_data) +{ + signal_buffer *record; + load_session(&record, address, user_data); + return record != NULL; +} + +int +delete_session(const signal_protocol_address *address, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, address->name); + if (!device_store) { + return SG_SUCCESS; + } + + return g_hash_table_remove(device_store, GINT_TO_POINTER(address->device_id)); +} + +int +delete_all_sessions(const char *name, size_t name_len, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, name); + if (!device_store) { + return SG_SUCCESS; + } + + guint len = g_hash_table_size(device_store); + g_hash_table_remove_all(device_store); + return len; +} + +int +load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data) +{ + GHashTable *pre_key_store = (GHashTable *)user_data; + + *record = g_hash_table_lookup(pre_key_store, GINT_TO_POINTER(pre_key_id)); + return SG_SUCCESS; +} + +int +store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, + void *user_data) +{ + GHashTable *pre_key_store = (GHashTable *)user_data; + + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(pre_key_store, GINT_TO_POINTER(pre_key_id), buffer); + return SG_SUCCESS; +} + +int +contains_pre_key(uint32_t pre_key_id, void *user_data) +{ + signal_buffer *record; + load_pre_key(&record, pre_key_id, user_data); + + return record != NULL; +} + +int +remove_pre_key(uint32_t pre_key_id, void *user_data) +{ + GHashTable *pre_key_store = (GHashTable *)user_data; + + return g_hash_table_remove(pre_key_store, GINT_TO_POINTER(pre_key_id)); +} + +int +load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, + void *user_data) +{ + GHashTable *signed_pre_key_store = (GHashTable *)user_data; + + *record = g_hash_table_lookup(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); + return SG_SUCCESS; +} + +int +store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, + size_t record_len, void *user_data) +{ + GHashTable *signed_pre_key_store = (GHashTable *)user_data; + + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id), buffer); + return SG_SUCCESS; +} + +int +contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) +{ + signal_buffer *record; + load_signed_pre_key(&record, signed_pre_key_id, user_data); + + return record != NULL; +} + +int +remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) +{ + GHashTable *signed_pre_key_store = (GHashTable *)user_data; + + return g_hash_table_remove(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); +} + +int +get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, + void *user_data) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + + *public_data = identity_key_store->public; + *private_data = identity_key_store->private; + + return SG_SUCCESS; +} + +int +get_local_registration_id(void *user_data, uint32_t *registration_id) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + + *registration_id = identity_key_store->registration_id; + + return SG_SUCCESS; +} + +int +save_identity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len, void *user_data) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + char *node = g_strdup_printf("%s:%d", address->name, address->device_id); + + signal_buffer *buffer = signal_buffer_create(key_data, key_len); + g_hash_table_insert(identity_key_store->identity_key_store, node, buffer); + + return SG_SUCCESS; +} + +int +is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len, void *user_data) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + char *node = g_strdup_printf("%s:%d", address->name, address->device_id); + + signal_buffer *buffer = signal_buffer_create(key_data, key_len); + signal_buffer *original = g_hash_table_lookup(identity_key_store->identity_key_store, node); + + return original == NULL || signal_buffer_compare(buffer, original); +} + +int +store_sender_key(const signal_protocol_sender_key_name *sender_key_name, + uint8_t *record, size_t record_len, uint8_t *user_record, + size_t user_record_len, void *user_data) +{ + return SG_SUCCESS; +} + +int +load_sender_key(signal_buffer **record, signal_buffer **user_record, + const signal_protocol_sender_key_name *sender_key_name, + void *user_data) +{ + return SG_SUCCESS; +} diff --git a/src/omemo/store.h b/src/omemo/store.h new file mode 100644 index 00000000..e99c514b --- /dev/null +++ b/src/omemo/store.h @@ -0,0 +1,34 @@ +#include + +typedef struct { + signal_buffer *public; + signal_buffer *private; + uint32_t registration_id; + GHashTable * identity_key_store; +} identity_key_store_t; + +GHashTable * session_store_new(void); +GHashTable * pre_key_store_new(void); +GHashTable * signed_pre_key_store_new(void); +void identity_key_store_new(identity_key_store_t *identity_key_store); + +int load_session(signal_buffer **record, const signal_protocol_address *address, void *user_data); +int get_sub_device_sessions(signal_int_list **sessions, const char *name, size_t name_len, void *user_data); +int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, void *user_data); +int contains_session(const signal_protocol_address *address, void *user_data); +int delete_session(const signal_protocol_address *address, void *user_data); +int delete_all_sessions(const char *name, size_t name_len, void *user_data); +int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data); +int store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data); +int contains_pre_key(uint32_t pre_key_id, void *user_data); +int remove_pre_key(uint32_t pre_key_id, void *user_data); +int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data); +int store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data); +int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data); +int remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data); +int get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data); +int get_local_registration_id(void *user_data, uint32_t *registration_id); +int save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data); +int is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data); +int store_sender_key(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data); +int load_sender_key(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name, void *user_data); diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 5418b1b4..f4641212 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -71,8 +71,80 @@ omemo_bundle_publish(void) } void -omemo_bundles_fetch(const char * const jid) +omemo_bundle_request(const char * const jid, uint32_t device_id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata) { + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *id = connection_create_stanza_id("devicelist_request"); + + xmpp_stanza_t *iq = stanza_create_omemo_bundle_request(ctx, id, jid, device_id); + iq_id_handler_add(id, func, free_func, userdata); + + iq_send_stanza(iq); + + free(id); + xmpp_stanza_release(iq); +} + +int +omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *const userdata) +{ + const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + if (!from) { + return 1; + } + + if (!g_strcmp0(from, userdata)) { + return 1; + } + + xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB); + if (!pubsub) { + return 1; + } + + xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(pubsub, "items"); + if (!items) { + return 1; + } + const char *node = xmpp_stanza_get_attribute(items, "node"); + char *device_id_str = strstr(node, ":"); + if (!device_id_str) { + return 1; + } + + uint32_t device_id = strtoul(device_id_str, NULL, 10); + + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); + if (!item) { + return 1; + } + + xmpp_stanza_t *bundle = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO); + if (!bundle) { + return 1; + } + + xmpp_stanza_t *prekeys = xmpp_stanza_get_child_by_name(bundle, "prekeys"); + if (!prekeys) { + return 1; + } + + /* Should be random */ + xmpp_stanza_t *prekey = xmpp_stanza_get_children(prekeys); + if (!prekey) { + return 1; + } + + xmpp_stanza_t *prekey_text = xmpp_stanza_get_children(prekey); + if (!prekey_text) { + return 1; + } + + size_t prekey_len; + unsigned char *prekey_raw = g_base64_decode(xmpp_stanza_get_text(prekey_text), &prekey_len); + + omemo_start_device_session(from, device_id, prekey_raw, prekey_len); + return 1; } static int diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index b125f76a..c384c4f0 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -1,6 +1,10 @@ #include +#include "xmpp/iq.h" + void omemo_devicelist_subscribe(void); void omemo_devicelist_publish(GList *device_list); void omemo_devicelist_request(const char * const jid); void omemo_bundle_publish(void); +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); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index d5993377..302258ec 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2284,6 +2284,31 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, return iq; } +xmpp_stanza_t* +stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, uint32_t device_id) +{ + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id); + xmpp_stanza_set_to(iq, jid); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *items = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(items, "items"); + char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, device_id); + xmpp_stanza_set_attribute(items, "node", node); + g_free(node); + + xmpp_stanza_add_child(pubsub, items); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(items); + xmpp_stanza_release(pubsub); + + return iq; +} + static void _stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 919379bb..bc157e46 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -193,6 +193,7 @@ #define STANZA_NS_COMMAND "http://jabber.org/protocol/commands" #define STANZA_NS_OMEMO "eu.siacs.conversations.axolotl" #define STANZA_NS_OMEMO_DEVICELIST "eu.siacs.conversations.axolotl.devicelist" +#define STANZA_NS_OMEMO_BUNDLES "eu.siacs.conversations.axolotl.bundles" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" @@ -292,6 +293,7 @@ xmpp_stanza_t* stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const cha xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids); xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length); +xmpp_stanza_t* stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, uint32_t device_id); int stanza_get_idle_time(xmpp_stanza_t *const stanza); -- cgit 1.4.1-2-gfad0 From b1ae220aa47ff503fe368e4802061be159dfb42b Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 26 Feb 2019 19:03:08 +0140 Subject: Create signal session --- src/omemo/omemo.c | 19 +++++++++++++++++-- src/omemo/omemo.h | 2 +- src/xmpp/omemo.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 32fdce42..95de1aa2 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -249,15 +249,30 @@ omemo_set_device_list(const char *const jid, GList * device_list) } void -omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const prekey, size_t prekey_len) +omemo_start_device_session(const char *const jid, uint32_t device_id, + uint32_t prekey_id, const unsigned char *const prekey_raw, size_t prekey_len, + uint32_t signed_prekey_id, const unsigned char *const signed_prekey_raw, + size_t signed_prekey_len, const unsigned char *const signature, + size_t signature_len, const unsigned char *const identity_key_raw, + size_t identity_key_len) { + session_pre_key_bundle *bundle; signal_protocol_address address = { jid, strlen(jid), device_id }; session_builder *builder; session_builder_create(&builder, omemo_ctx.store, &address, omemo_ctx.signal); - //session_builder_process_pre_key_bundle(builder, prekey); + + ec_public_key *prekey; + curve_decode_point(&prekey, prekey_raw, prekey_len, omemo_ctx.signal); + ec_public_key *signed_prekey; + curve_decode_point(&signed_prekey, signed_prekey_raw, signed_prekey_len, omemo_ctx.signal); + ec_public_key *identity_key; + curve_decode_point(&identity_key, identity_key_raw, identity_key_len, omemo_ctx.signal); + + session_pre_key_bundle_create(&bundle, 0, device_id, prekey_id, prekey, signed_prekey_id, signed_prekey, signature, signature_len, identity_key); + session_builder_process_pre_key_bundle(builder, bundle); } static void diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 26bd6e65..bf42b3e3 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -15,6 +15,6 @@ void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); void omemo_start_session(const char *const barejid); -void omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const prekey, size_t prekey_len); +void omemo_start_device_session(const char *const jid, uint32_t device_id, uint32_t prekey_id, const unsigned char *const prekey, size_t prekey_len, 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); diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index f4641212..709b6ad8 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -134,16 +134,60 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons if (!prekey) { return 1; } - + const char *prekey_id_text = xmpp_stanza_get_attribute(prekey, "preKeyId"); + if (!prekey_id_text) { + return 1; + } + uint32_t prekey_id = strtoul(prekey_id_text, NULL, 10); xmpp_stanza_t *prekey_text = xmpp_stanza_get_children(prekey); if (!prekey_text) { return 1; } - size_t prekey_len; unsigned char *prekey_raw = g_base64_decode(xmpp_stanza_get_text(prekey_text), &prekey_len); - omemo_start_device_session(from, device_id, prekey_raw, prekey_len); + xmpp_stanza_t *signed_prekey = xmpp_stanza_get_child_by_name(bundle, "signedPreKeyPublic"); + if (!signed_prekey) { + return 1; + } + const char *signed_prekey_id_text = xmpp_stanza_get_attribute(signed_prekey, "signedPreKeyId"); + if (!signed_prekey_id_text) { + return 1; + } + uint32_t signed_prekey_id = strtoul(signed_prekey_id_text, NULL, 10); + xmpp_stanza_t *signed_prekey_text = xmpp_stanza_get_children(signed_prekey); + if (!signed_prekey_text) { + return 1; + } + size_t signed_prekey_len; + unsigned char *signed_prekey_raw = g_base64_decode(xmpp_stanza_get_text(signed_prekey_text), &signed_prekey_len); + + xmpp_stanza_t *signed_prekey_signature = xmpp_stanza_get_child_by_name(bundle, "signedPreKeySignature"); + if (!signed_prekey_signature) { + return 1; + } + xmpp_stanza_t *signed_prekey_signature_text = xmpp_stanza_get_children(signed_prekey_signature); + if (!signed_prekey_signature_text) { + return 1; + } + size_t signed_prekey_signature_len; + unsigned char *signed_prekey_signature_raw = g_base64_decode(xmpp_stanza_get_text(signed_prekey_signature_text), &signed_prekey_signature_len); + + xmpp_stanza_t *identity_key = xmpp_stanza_get_child_by_name(bundle, "identityKey"); + if (!identity_key) { + return 1; + } + xmpp_stanza_t *identity_key_text = xmpp_stanza_get_children(identity_key); + if (!identity_key_text) { + return 1; + } + size_t identity_key_len; + unsigned char *identity_key_raw = g_base64_decode(xmpp_stanza_get_text(identity_key_text), &identity_key_len); + + omemo_start_device_session(from, device_id, prekey_id, prekey_raw, + prekey_len, signed_prekey_id, signed_prekey_raw, signed_prekey_len, + signed_prekey_signature_raw, signed_prekey_signature_len, + identity_key_raw, identity_key_len); return 1; } -- cgit 1.4.1-2-gfad0 From 0fb27dc4961608eb9b088ca659eb087dd2c1cae7 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 26 Feb 2019 20:33:06 +0140 Subject: Add OMEMO message encryption and decryption --- configure.ac | 8 ++ src/command/cmd_funcs.c | 1 + src/config/preferences.c | 28 +++++++ src/config/preferences.h | 3 + src/event/client_events.c | 19 +++++ src/log.c | 34 ++++++++ src/log.h | 2 + src/omemo/crypto.c | 194 ++++++++++++++++++++++++++++++++++++++++---- src/omemo/crypto.h | 14 +++- src/omemo/omemo.c | 172 +++++++++++++++++++++++++++++++++++++-- src/omemo/omemo.h | 12 +++ src/omemo/store.c | 12 ++- src/ui/chatwin.c | 2 + src/ui/ui.h | 3 +- src/ui/window.c | 2 + src/xmpp/message.c | 203 +++++++++++++++++++++++++++++++++++++++++++++- src/xmpp/omemo.c | 4 +- src/xmpp/xmpp.h | 1 + 18 files changed, 680 insertions(+), 34 deletions(-) diff --git a/configure.ac b/configure.ac index e886510e..cc53254f 100644 --- a/configure.ac +++ b/configure.ac @@ -283,6 +283,14 @@ if test "x$enable_omemo" != xno; then [AS_IF([test "x$enable_omemo" = xyes], [AC_MSG_ERROR([libsodium is required for omemo support])], [AC_MSG_NOTICE([libsodium not found, omemo support not enabled])])]) + + AC_CHECK_LIB([gcrypt], [gcry_check_version], + [AM_CONDITIONAL([BUILD_OMEMO], [true]) + AC_DEFINE([HAVE_OMEMO], [1], [Have omemo]), + LIBS="-lgcrypt $LIBS"], + [AS_IF([test "x$enable_omemo" = xyes], + [AC_MSG_ERROR([gcrypt is required for omemo support])], + [AC_MSG_NOTICE([gcrypt not found, omemo support not enabled])])]) fi AS_IF([test "x$with_themes" = xno], diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 66715d20..de3372cd 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7949,6 +7949,7 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) } omemo_start_session(barejid); + chatwin->is_omemo = TRUE; } return TRUE; diff --git a/src/config/preferences.c b/src/config/preferences.c index 265e11db..7d0c91fc 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -58,6 +58,7 @@ #define PREF_GROUP_ALIAS "alias" #define PREF_GROUP_OTR "otr" #define PREF_GROUP_PGP "pgp" +#define PREF_GROUP_OMEMO "omemo" #define PREF_GROUP_MUC "muc" #define PREF_GROUP_PLUGINS "plugins" @@ -822,6 +823,33 @@ prefs_set_pgp_char(char ch) _save_prefs(); } +char +prefs_get_omemo_char(void) +{ + char result = '~'; + + char *resultstr = g_key_file_get_string(prefs, PREF_GROUP_OMEMO, "omemo.char", NULL); + if (!resultstr) { + result = '~'; + } else { + result = resultstr[0]; + } + free(resultstr); + + return result; +} + +void +prefs_set_omemo_char(char ch) +{ + char str[2]; + str[0] = ch; + str[1] = '\0'; + + g_key_file_set_string(prefs, PREF_GROUP_OMEMO, "omemo.char", str); + _save_prefs(); +} + char prefs_get_roster_header_char(void) { diff --git a/src/config/preferences.h b/src/config/preferences.h index 65dee327..a4d82967 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -148,6 +148,7 @@ typedef enum { PREF_STATUSBAR_SELF, PREF_STATUSBAR_CHAT, PREF_STATUSBAR_ROOM, + PREF_OMEMO_LOG, } preference_t; typedef struct prof_alias_t { @@ -216,6 +217,8 @@ char prefs_get_otr_char(void); void prefs_set_otr_char(char ch); char prefs_get_pgp_char(void); void prefs_set_pgp_char(char ch); +char prefs_get_omemo_char(void); +void prefs_set_omemo_char(char ch); char prefs_get_roster_header_char(void); void prefs_set_roster_header_char(char ch); diff --git a/src/event/client_events.c b/src/event/client_events.c index 3b6218ea..76a38b15 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -54,6 +54,10 @@ #include "pgp/gpg.h" #endif +#ifdef HAVE_OMEMO +#include "omemo/omemo.h" +#endif + jabber_conn_status_t cl_ev_connect_jid(const char *const jid, const char *const passwd, const char *const altdomain, const int port, const char *const tls_policy) { @@ -203,6 +207,21 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #endif #endif +#ifdef HAVE_OMEMO + if (chatwin->is_omemo) { + omemo_on_message_send(chatwin, plugin_msg, request_receipt); + } else { + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + chat_log_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); + free(id); + } + + plugins_post_chat_message_send(chatwin->barejid, plugin_msg); + free(plugin_msg); + return; +#endif + // OTR unsupported, PGP unsupported #ifndef HAVE_LIBOTR #ifndef HAVE_LIBGPGME diff --git a/src/log.c b/src/log.c index 0133a6cf..090fd2f8 100644 --- a/src/log.c +++ b/src/log.c @@ -304,6 +304,23 @@ chat_log_pgp_msg_out(const char *const barejid, const char *const msg) } } +void +chat_log_omemo_msg_out(const char *const barejid, const char *const msg) +{ + if (prefs_get_boolean(PREF_CHLOG)) { + const char *jid = connection_get_fulljid(); + Jid *jidp = jid_create(jid); + char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG); + if (strcmp(pref_omemo_log, "on") == 0) { + _chat_log_chat(jidp->barejid, barejid, msg, PROF_OUT_LOG, NULL); + } else if (strcmp(pref_omemo_log, "redact") == 0) { + _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_OUT_LOG, NULL); + } + prefs_free_string(pref_omemo_log); + jid_destroy(jidp); + } +} + void chat_log_otr_msg_in(const char *const barejid, const char *const msg, gboolean was_decrypted, GDateTime *timestamp) { @@ -338,6 +355,23 @@ chat_log_pgp_msg_in(const char *const barejid, const char *const msg, GDateTime } } +void +chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp) +{ + if (prefs_get_boolean(PREF_CHLOG)) { + const char *jid = connection_get_fulljid(); + Jid *jidp = jid_create(jid); + char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG); + if (strcmp(pref_omemo_log, "on") == 0) { + _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, timestamp); + } else if (strcmp(pref_omemo_log, "redact") == 0) { + _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_IN_LOG, timestamp); + } + prefs_free_string(pref_omemo_log); + jid_destroy(jidp); + } +} + void chat_log_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp) { diff --git a/src/log.h b/src/log.h index 43a34ca1..b14231b7 100644 --- a/src/log.h +++ b/src/log.h @@ -71,10 +71,12 @@ void chat_log_init(void); void chat_log_msg_out(const char *const barejid, const char *const msg); void chat_log_otr_msg_out(const char *const barejid, const char *const msg); void chat_log_pgp_msg_out(const char *const barejid, const char *const msg); +void chat_log_omemo_msg_out(const char *const barejid, const char *const msg); void chat_log_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp); void chat_log_otr_msg_in(const char *const barejid, const char *const msg, gboolean was_decrypted, GDateTime *timestamp); void chat_log_pgp_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp); +void chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp); void chat_log_close(void); GSList* chat_log_get_previous(const gchar *const login, const gchar *const recipient); diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index a986c729..73b2ba0d 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -2,7 +2,9 @@ #include #include #include +#include +#include "omemo/omemo.h" #include "omemo/crypto.h" int @@ -12,10 +14,12 @@ omemo_crypto_init(void) return -1; } - if (crypto_aead_aes256gcm_is_available() == 0) { + if (!gcry_check_version(GCRYPT_VERSION)) { return -1; } + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + return 0; } @@ -96,41 +100,195 @@ int omemo_encrypt_func(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data) { + gcry_cipher_hd_t hd; + unsigned char *padded_plaintext; unsigned char *ciphertext; - unsigned long long ciphertext_len; + size_t ciphertext_len; + int mode; + int algo; + uint8_t padding = 0; + + switch (key_len) { + case 32: + algo = GCRY_CIPHER_AES256; + break; + default: + return OMEMO_ERR_UNSUPPORTED_CRYPTO; + } - assert(cipher != SG_CIPHER_AES_GCM_NOPADDING); - assert(key_len == crypto_aead_aes256gcm_KEYBYTES); - assert(iv_len == crypto_aead_aes256gcm_NPUBBYTES); + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + mode = GCRY_CIPHER_MODE_CBC; + break; + default: + return OMEMO_ERR_UNSUPPORTED_CRYPTO; + } - ciphertext = malloc(plaintext_len + crypto_aead_aes256gcm_ABYTES); - crypto_aead_aes256gcm_encrypt(ciphertext, &ciphertext_len, plaintext, plaintext_len, NULL, 0, NULL, iv, key); + gcry_cipher_open(&hd, algo, mode, GCRY_CIPHER_SECURE); + + gcry_cipher_setkey(hd, key, key_len); + + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + gcry_cipher_setiv(hd, iv, iv_len); + padding = 16 - (plaintext_len % 16); + break; + default: + assert(FALSE); + } + + padded_plaintext = malloc(plaintext_len + padding); + memcpy(padded_plaintext, plaintext, plaintext_len); + memset(padded_plaintext + plaintext_len, padding, padding); + + ciphertext_len = plaintext_len + padding; + ciphertext = malloc(ciphertext_len); + gcry_cipher_encrypt(hd, ciphertext, ciphertext_len, padded_plaintext, plaintext_len + padding); *output = signal_buffer_create(ciphertext, ciphertext_len); + free(padded_plaintext); free(ciphertext); - return 0; + gcry_cipher_close(hd); + + return SG_SUCCESS; } int omemo_decrypt_func(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data) { + int ret = SG_SUCCESS; + gcry_cipher_hd_t hd; unsigned char *plaintext; - unsigned long long plaintext_len; + size_t plaintext_len; + int mode; + int algo; + uint8_t padding = 0; - assert(cipher != SG_CIPHER_AES_GCM_NOPADDING); - assert(key_len == crypto_aead_aes256gcm_KEYBYTES); - assert(iv_len == crypto_aead_aes256gcm_NPUBBYTES); + switch (key_len) { + case 32: + algo = GCRY_CIPHER_AES256; + break; + default: + return OMEMO_ERR_UNSUPPORTED_CRYPTO; + } - plaintext = malloc(ciphertext_len - crypto_aead_aes256gcm_ABYTES); - if (crypto_aead_aes256gcm_decrypt(plaintext, &plaintext_len, NULL, ciphertext, ciphertext_len, NULL, 0, iv, key) < 0) { - free(plaintext); - return -1; + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + mode = GCRY_CIPHER_MODE_CBC; + break; + default: + return OMEMO_ERR_UNSUPPORTED_CRYPTO; } - *output = signal_buffer_create(plaintext, plaintext_len); + gcry_cipher_open(&hd, algo, mode, GCRY_CIPHER_SECURE); + + gcry_cipher_setkey(hd, key, key_len); + + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + gcry_cipher_setiv(hd, iv, iv_len); + break; + default: + assert(FALSE); + } + + plaintext_len = ciphertext_len; + plaintext = malloc(plaintext_len); + gcry_cipher_decrypt(hd, plaintext, plaintext_len, ciphertext, ciphertext_len); + + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + padding = plaintext[plaintext_len - 1]; + break; + default: + assert(FALSE); + } + + int i; + for (i = 0; i < padding; i++) { + if (plaintext[plaintext_len - 1 - i] != padding) { + ret = SG_ERR_UNKNOWN; + goto out; + } + } + + *output = signal_buffer_create(plaintext, plaintext_len - padding); + +out: free(plaintext); - return 0; + gcry_cipher_close(hd); + + return ret; +} + +int +aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len, const unsigned char *const plaintext, size_t plaintext_len, const unsigned char *const iv, const unsigned char *const key) +{ + gcry_error_t res; + gcry_cipher_hd_t hd; + + res = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + res = gcry_cipher_setkey(hd, key, AES128_GCM_KEY_LENGTH); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + res = gcry_cipher_setiv(hd, iv, AES128_GCM_IV_LENGTH); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + + res = gcry_cipher_encrypt(hd, ciphertext, *ciphertext_len, plaintext, plaintext_len); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + + res = gcry_cipher_gettag(hd, ciphertext + plaintext_len, AES128_GCM_TAG_LENGTH); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + +out: + gcry_cipher_close(hd); + return res; +} + +int +aes128gcm_decrypt(unsigned char *plaintext, size_t *plaintext_len, const unsigned char *const ciphertext, size_t ciphertext_len, const unsigned char *const iv, const unsigned char *const key) +{ + gcry_error_t res; + gcry_cipher_hd_t hd; + + res = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + + res = gcry_cipher_setkey(hd, key, AES128_GCM_KEY_LENGTH); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + + res = gcry_cipher_setiv(hd, iv, AES128_GCM_IV_LENGTH); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + + res = gcry_cipher_decrypt(hd, plaintext, *plaintext_len, ciphertext, ciphertext_len); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + //res = gcry_cipher_checktag(hd, ciphertext + ciphertext_len - AES128_GCM_TAG_LENGTH, AES128_GCM_TAG_LENGTH); + //if (res != GPG_ERR_NO_ERROR) { + // goto out; + //} + +out: + gcry_cipher_close(hd); + return res; } diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index 759cef69..35c5d72a 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -1,6 +1,8 @@ #include -#define SG_CIPHER_AES_GCM_NOPADDING 1000 +#define AES128_GCM_KEY_LENGTH 16 +#define AES128_GCM_IV_LENGTH 16 +#define AES128_GCM_TAG_LENGTH 16 int omemo_crypto_init(void); /** @@ -134,3 +136,13 @@ int omemo_decrypt_func(signal_buffer **output, const uint8_t *iv, size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data); + +int aes128gcm_encrypt(unsigned char *ciphertext, + size_t *ciphertext_len, const unsigned char *const cleartext, + size_t cleatext_len, const unsigned char *const iv, + const unsigned char *const key); + +int aes128gcm_decrypt(unsigned char *plaintext, + size_t *plaintext_len, const unsigned char *const ciphertext, + size_t ciphertext_len, const unsigned char *const iv, + const unsigned char *const key); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 95de1aa2..a9860851 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include #include "config/account.h" @@ -21,6 +23,7 @@ static gboolean loaded; static void lock(void *user_data); static void unlock(void *user_data); +static void omemo_log(int level, const char *message, size_t len, void *user_data); struct omemo_context_t { pthread_mutexattr_t attr; @@ -37,6 +40,7 @@ struct omemo_context_t { GHashTable *pre_key_store; GHashTable *signed_pre_key_store; identity_key_store_t identity_key_store; + GHashTable *device_ids; }; static omemo_context omemo_ctx; @@ -73,6 +77,10 @@ omemo_init(void) return; } + if (signal_context_set_log_function(omemo_ctx.signal, omemo_log) != 0) { + cons_show("Error initializing OMEMO log"); + } + if (signal_context_set_crypto_provider(omemo_ctx.signal, &crypto_provider) != 0) { cons_show("Error initializing OMEMO crypto"); return; @@ -123,6 +131,8 @@ omemo_init(void) .get_local_registration_id = get_local_registration_id, .save_identity = save_identity, .is_trusted_identity = is_trusted_identity, + .destroy_func = NULL, + .user_data = &omemo_ctx.identity_key_store }; signal_protocol_store_context_set_identity_key_store(omemo_ctx.store, &identity_key_store); @@ -148,6 +158,9 @@ omemo_generate_crypto_materials(ProfAccount *account) unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; signal_protocol_key_helper_generate_signed_pre_key(&omemo_ctx.signed_pre_key, omemo_ctx.identity_key_pair, 5, timestamp, omemo_ctx.signal); + ec_public_key_serialize(&omemo_ctx.identity_key_store.public, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair)); + ec_private_key_serialize(&omemo_ctx.identity_key_store.private, ratchet_identity_key_pair_get_private(omemo_ctx.identity_key_pair)); + loaded = TRUE; /* Ensure we get our current device list, and it gets updated with our @@ -191,7 +204,7 @@ omemo_identity_key(unsigned char **output, size_t *length) ec_public_key_serialize(&buffer, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair)); *length = signal_buffer_len(buffer); *output = malloc(*length); - memcpy(*output, signal_buffer_const_data(buffer), *length); + memcpy(*output, signal_buffer_data(buffer), *length); signal_buffer_free(buffer); } @@ -202,7 +215,7 @@ omemo_signed_prekey(unsigned char **output, size_t *length) ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(omemo_ctx.signed_pre_key))); *length = signal_buffer_len(buffer); *output = malloc(*length); - memcpy(*output, signal_buffer_const_data(buffer), *length); + memcpy(*output, signal_buffer_data(buffer), *length); signal_buffer_free(buffer); } @@ -224,7 +237,7 @@ omemo_prekeys(GList **prekeys, GList **ids, GList **lengths) ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_pre_key_get_key_pair(prekey))); size_t length = signal_buffer_len(buffer); unsigned char *prekey_value = malloc(length); - memcpy(prekey_value, signal_buffer_const_data(buffer), length); + memcpy(prekey_value, signal_buffer_data(buffer), length); signal_buffer_free(buffer); *prekeys = g_list_append(*prekeys, prekey_value); *ids = g_list_append(*ids, GINT_TO_POINTER(session_pre_key_get_id(prekey))); @@ -257,12 +270,15 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, size_t identity_key_len) { session_pre_key_bundle *bundle; - signal_protocol_address address = { - jid, strlen(jid), device_id - }; + signal_protocol_address *address; + + address = malloc(sizeof(signal_protocol_address)); + address->name = strdup(jid); + address->name_len = strlen(jid); + address->device_id = device_id; session_builder *builder; - session_builder_create(&builder, omemo_ctx.store, &address, omemo_ctx.signal); + session_builder_create(&builder, omemo_ctx.store, address, omemo_ctx.signal); ec_public_key *prekey; curve_decode_point(&prekey, prekey_raw, prekey_len, omemo_ctx.signal); @@ -275,6 +291,142 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, session_builder_process_pre_key_bundle(builder, bundle); } +gboolean +omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) +{ + int res; + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + GList *device_ids = NULL; + + GList *recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, chatwin->barejid); + if (!recipient_device_id) { + return FALSE; + } + device_ids = g_list_copy(recipient_device_id); + + GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, barejid); + device_ids = g_list_concat(device_ids, g_list_copy(sender_device_id)); + + /* TODO generate fresh AES-GCM materials */ + /* TODO encrypt message */ + unsigned char *key; + unsigned char *iv; + unsigned char *ciphertext; + size_t ciphertext_len; + + key = sodium_malloc(AES128_GCM_KEY_LENGTH); + iv = sodium_malloc(AES128_GCM_IV_LENGTH); + ciphertext_len = strlen(message) + AES128_GCM_TAG_LENGTH; + ciphertext = malloc(ciphertext_len); + + randombytes_buf(key, 16); + randombytes_buf(iv, 16); + + res = aes128gcm_encrypt(ciphertext, &ciphertext_len, (const unsigned char * const)message, strlen(message), iv, key); + if (res != 0) { + return FALSE; + } + + GList *keys = NULL; + GList *device_ids_iter; + for (device_ids_iter = device_ids; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { + int res; + ciphertext_message *ciphertext; + session_cipher *cipher; + signal_protocol_address address = { + chatwin->barejid, strlen(chatwin->barejid), GPOINTER_TO_INT(device_ids_iter->data) + }; + + res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); + if (res != 0) { + continue; + } + + res = session_cipher_encrypt(cipher, key, AES128_GCM_KEY_LENGTH, &ciphertext); + if (res != 0) { + continue; + } + signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + key->data = signal_buffer_data(buffer); + key->length = signal_buffer_len(buffer); + key->device_id = GPOINTER_TO_INT(device_ids_iter->data); + key->prekey = TRUE; + keys = g_list_append(keys, key); + } + + char *id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt); + chat_log_omemo_msg_out(chatwin->barejid, message); + chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OMEMO, request_receipt); + + free(id); + g_list_free_full(keys, free); + free(ciphertext); + sodium_free(key); + sodium_free(iv); + g_list_free(device_ids); + + return TRUE; +} + +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) +{ + int res; + GList *key_iter; + omemo_key_t *key = NULL; + for (key_iter = keys; key_iter != NULL; key_iter = key_iter->next) { + if (((omemo_key_t *)key_iter->data)->device_id == omemo_ctx.device_id) { + key = key_iter->data; + break; + } + } + + if (!key) { + return NULL; + } + + session_cipher *cipher; + signal_buffer *plaintext_key; + signal_protocol_address address = { + from, strlen(from), sid + }; + + res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); + if (res != 0) { + return NULL; + } + + if (key->prekey) { + pre_key_signal_message *message; + pre_key_signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); + res = session_cipher_decrypt_pre_key_signal_message(cipher, message, NULL, &plaintext_key); + } else { + signal_message *message; + signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); + res = session_cipher_decrypt_signal_message(cipher, message, NULL, &plaintext_key); + } + if (res != 0) { + return NULL; + } + + size_t plaintext_len = payload_len; + unsigned char *plaintext = malloc(plaintext_len + 1); + res = aes128gcm_decrypt(plaintext, &plaintext_len, payload, payload_len, iv, signal_buffer_data(plaintext_key)); + if (res != 0) { + free(plaintext); + return NULL; + } + + plaintext[plaintext_len] = '\0'; + + return (char *)plaintext; + +} + static void lock(void *user_data) { @@ -288,3 +440,9 @@ unlock(void *user_data) omemo_context *ctx = (omemo_context *)user_data; pthread_mutex_unlock(&ctx->lock); } + +static void +omemo_log(int level, const char *message, size_t len, void *user_data) +{ + cons_show(message); +} diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index bf42b3e3..eb9569a3 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -1,9 +1,19 @@ #include +#include "ui/ui.h" #include "config/account.h" +#define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000 + typedef struct omemo_context_t omemo_context; +typedef struct omemo_key { + const unsigned char *data; + size_t length; + gboolean prekey; + uint32_t device_id; +} omemo_key_t; + void omemo_init(void); void omemo_generate_crypto_materials(ProfAccount *account); @@ -18,3 +28,5 @@ void omemo_start_session(const char *const barejid); void omemo_start_device_session(const char *const jid, uint32_t device_id, uint32_t prekey_id, const unsigned char *const prekey, size_t prekey_len, 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); +gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt); +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); diff --git a/src/omemo/store.c b/src/omemo/store.c index 384eb9ce..ab8cd81b 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -39,12 +39,16 @@ load_session(signal_buffer **record, const signal_protocol_address *address, device_store = g_hash_table_lookup(session_store, address->name); if (!device_store) { *record = NULL; - return SG_SUCCESS; + return 0; } signal_buffer *original = g_hash_table_lookup(device_store, GINT_TO_POINTER(address->device_id)); + if (!original) { + *record = NULL; + return 0; + } *record = signal_buffer_copy(original); - return SG_SUCCESS; + return 1; } int @@ -208,8 +212,8 @@ get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, { identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; - *public_data = identity_key_store->public; - *private_data = identity_key_store->private; + *public_data = signal_buffer_copy(identity_key_store->public); + *private_data = signal_buffer_copy(identity_key_store->private); return SG_SUCCESS; } diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index 98431a60..c7acce52 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -305,6 +305,8 @@ chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, enc_char = prefs_get_otr_char(); } else if (enc_mode == PROF_MSG_PGP) { enc_char = prefs_get_pgp_char(); + } else if (enc_mode == PROF_MSG_OMEMO) { + enc_char = prefs_get_omemo_char(); } if (request_receipt && id) { diff --git a/src/ui/ui.h b/src/ui/ui.h index ad5a1216..95d291b4 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -56,7 +56,8 @@ typedef enum { PROF_MSG_PLAIN, PROF_MSG_OTR, - PROF_MSG_PGP + PROF_MSG_PGP, + PROF_MSG_OMEMO } prof_enc_t; // core UI diff --git a/src/ui/window.c b/src/ui/window.c index 5693e35f..ef0f93d2 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -1058,6 +1058,8 @@ win_print_incoming(ProfWin *window, GDateTime *timestamp, enc_char = prefs_get_otr_char(); } else if (enc_mode == PROF_MSG_PGP) { enc_char = prefs_get_pgp_char(); + } else if (enc_mode == PROF_MSG_OMEMO) { + enc_char = prefs_get_omemo_char(); } _win_printf(window, enc_char, 0, timestamp, NO_ME, THEME_TEXT_THEM, from, "%s", message); break; diff --git a/src/xmpp/message.c b/src/xmpp/message.c index fba62bc8..bf4e6a2f 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -52,6 +52,7 @@ #include "pgp/gpg.h" #include "plugins/plugins.h" #include "ui/ui.h" +#include "ui/window_list.h" #include "xmpp/chat_session.h" #include "xmpp/muc.h" #include "xmpp/session.h" @@ -62,6 +63,10 @@ #include "xmpp/connection.h" #include "xmpp/xmpp.h" +#ifdef HAVE_OMEMO +#include "omemo/omemo.h" +#endif + typedef struct p_message_handle_t { ProfMessageCallback func; ProfMessageFreeCallback free_func; @@ -77,6 +82,7 @@ static void _handle_conference(xmpp_stanza_t *const stanza); static void _handle_captcha(xmpp_stanza_t *const stanza); static void _handle_receipt_received(xmpp_stanza_t *const stanza); static void _handle_chat(xmpp_stanza_t *const stanza); +static void _handle_omemo(xmpp_stanza_t *const stanza); static void _send_message_stanza(xmpp_stanza_t *const stanza); @@ -144,6 +150,13 @@ _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *con } } +#ifdef HAVE_OMEMO + xmpp_stanza_t *omemo = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); + if (omemo) { + _handle_omemo(stanza); + } +#endif + _handle_chat(stanza); return 1; @@ -307,6 +320,99 @@ message_send_chat_otr(const char *const barejid, const char *const msg, gboolean return id; } +#ifdef HAVE_OMEMO +char* +message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, + const unsigned char *const iv, size_t iv_len, + const unsigned char *const ciphertext, size_t ciphertext_len, + gboolean request_receipt) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *id = connection_create_stanza_id("msg"); + + xmpp_stanza_t *message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id); + + xmpp_stanza_t *encrypted = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(encrypted, "encrypted"); + xmpp_stanza_set_ns(encrypted, STANZA_NS_OMEMO); + + xmpp_stanza_t *header = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(header, "header"); + char *sid_text = g_strdup_printf("%d", sid); + xmpp_stanza_set_attribute(header, "sid", sid_text); + g_free(sid_text); + + GList *key_iter; + for (key_iter = keys; key_iter != NULL; key_iter = key_iter->next) { + omemo_key_t *key = (omemo_key_t *)key_iter->data; + + xmpp_stanza_t *key_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(key_stanza, "key"); + char *rid = g_strdup_printf("%d", key->device_id); + xmpp_stanza_set_attribute(key_stanza, "rid", rid); + g_free(rid); + if (key->prekey) { + xmpp_stanza_set_attribute(key_stanza, "prekey", "true"); + } + + gchar *key_raw = g_base64_encode(key->data, key->length); + xmpp_stanza_t *key_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(key_text, key_raw); + g_free(key_raw); + + xmpp_stanza_add_child(key_stanza, key_text); + xmpp_stanza_add_child(header, key_stanza); + xmpp_stanza_release(key_text); + xmpp_stanza_release(key_stanza); + } + + xmpp_stanza_t *iv_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(iv_stanza, "iv"); + + gchar *iv_raw = g_base64_encode(iv, iv_len); + xmpp_stanza_t *iv_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(iv_text, iv_raw); + g_free(iv_raw); + + xmpp_stanza_add_child(iv_stanza, iv_text); + xmpp_stanza_add_child(header, iv_stanza); + xmpp_stanza_release(iv_text); + xmpp_stanza_release(iv_stanza); + + xmpp_stanza_add_child(encrypted, header); + xmpp_stanza_release(header); + + xmpp_stanza_t *payload = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(payload, "payload"); + + gchar *ciphertext_raw = g_base64_encode(ciphertext, ciphertext_len); + xmpp_stanza_t *payload_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(payload_text, ciphertext_raw); + g_free(ciphertext_raw); + + xmpp_stanza_add_child(payload, payload_text); + xmpp_stanza_add_child(encrypted, payload); + xmpp_stanza_release(payload_text); + xmpp_stanza_release(payload); + + xmpp_stanza_add_child(message, encrypted); + xmpp_stanza_release(encrypted); + + stanza_attach_carbons_private(ctx, message); + stanza_attach_hints_no_copy(ctx, message); + stanza_attach_hints_no_store(ctx, message); + + if (request_receipt) { + stanza_attach_receipt_request(ctx, message); + } + + _send_message_stanza(message); + xmpp_stanza_release(message); + + return id; +} +#endif + void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url) { @@ -828,7 +934,8 @@ _handle_chat(xmpp_stanza_t *const stanza) // ignore handled namespaces xmpp_stanza_t *conf = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CONFERENCE); xmpp_stanza_t *captcha = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CAPTCHA); - if (conf || captcha) { + xmpp_stanza_t *omemo = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); + if (conf || captcha || omemo) { return; } @@ -896,6 +1003,100 @@ _handle_chat(xmpp_stanza_t *const stanza) jid_destroy(jid); } +static void +_handle_omemo(xmpp_stanza_t *const stanza) +{ + xmpp_stanza_t *encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); + if (!encrypted) { + return; + } + + xmpp_stanza_t *header = xmpp_stanza_get_child_by_name(encrypted, "header"); + if (!header) { + return; + } + + const char *sid_text = xmpp_stanza_get_attribute(header, "sid"); + if (!sid_text) { + return; + } + uint32_t sid = strtoul(sid_text, NULL, 10); + + xmpp_stanza_t *iv = xmpp_stanza_get_child_by_name(header, "iv"); + if (!iv) { + return; + } + const char *iv_text = xmpp_stanza_get_text(iv); + if (!iv_text) { + return; + } + size_t iv_len; + const unsigned char *iv_raw = g_base64_decode(iv_text, &iv_len); + + xmpp_stanza_t *payload = xmpp_stanza_get_child_by_name(encrypted, "payload"); + if (!payload) { + return; + } + const char *payload_text = xmpp_stanza_get_text(payload); + if (!payload_text) { + return; + } + size_t payload_len; + const unsigned char *payload_raw = g_base64_decode(payload_text, &payload_len); + + GList *keys = NULL; + xmpp_stanza_t *key_stanza; + for (key_stanza = xmpp_stanza_get_children(header); key_stanza != NULL; key_stanza = xmpp_stanza_get_next(key_stanza)) { + if (g_strcmp0(xmpp_stanza_get_name(key_stanza), "key") != 0) { + continue; + } + + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + const char *key_text = xmpp_stanza_get_text(key_stanza); + if (!key_text) { + goto skip; + } + + + const char *rid_text = xmpp_stanza_get_attribute(key_stanza, "rid"); + key->device_id = strtoul(rid_text, NULL, 10); + if (!key->device_id) { + goto skip; + } + key->data = g_base64_decode(key_text, &key->length); + key->prekey = g_strcmp0(xmpp_stanza_get_attribute(key_stanza, "prekey"), "true") == 0; + keys = g_list_append(keys, key); + continue; + +skip: + free(key); + } + + const char *from = xmpp_stanza_get_from(stanza); + Jid *jid = jid_create(from); + GDateTime *timestamp = stanza_get_delay(stanza); + + char *plaintext = omemo_on_message_recv(jid->barejid, sid, iv_raw, iv_len, keys, payload_raw, payload_len); + if (!plaintext) { + goto out; + } + + gboolean new_win = FALSE; + ProfChatWin *chatwin = wins_get_chat(jid->barejid); + if (!chatwin) { + ProfWin *window = wins_new_chat(jid->barejid); + chatwin = (ProfChatWin*)window; + new_win = TRUE; + } + + chat_log_omemo_msg_in(jid->barejid, plaintext, timestamp); + chatwin_incoming_msg(chatwin, jid->resourcepart, plaintext, timestamp, new_win, PROF_MSG_OMEMO); + +out: + jid_destroy(jid); + if (timestamp) g_date_time_unref(timestamp); +} + static void _send_message_stanza(xmpp_stanza_t *const stanza) { diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 709b6ad8..048126fa 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -93,7 +93,7 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons return 1; } - if (!g_strcmp0(from, userdata)) { + if (g_strcmp0(from, userdata) != 0) { return 1; } @@ -112,7 +112,7 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons return 1; } - uint32_t device_id = strtoul(device_id_str, NULL, 10); + uint32_t device_id = strtoul(++device_id_str, NULL, 10); xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); if (!item) { diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 5c0dae76..f2eec6c7 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -140,6 +140,7 @@ char* message_send_chat(const char *const barejid, const char *const msg, const gboolean request_receipt); char* message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt); char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt); +char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt); void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url); void message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url); void message_send_groupchat_subject(const char *const roomjid, const char *const subject); -- cgit 1.4.1-2-gfad0 From 904904d74e138207f08ff4fefb0c307f02c22ff2 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 28 Feb 2019 20:07:03 +0140 Subject: Add default body on OMEMO message --- src/xmpp/message.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index bf4e6a2f..5bad4980 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -398,6 +398,14 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, xmpp_stanza_add_child(message, encrypted); xmpp_stanza_release(encrypted); + xmpp_stanza_t *body = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(body, "body"); + xmpp_stanza_t *body_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(body_text, "You received a message encrypted with OMEMO but your client doesn't support OMEMO."); + xmpp_stanza_add_child(body, body_text); + xmpp_stanza_release(body_text); + xmpp_stanza_add_child(message, body); + stanza_attach_carbons_private(ctx, message); stanza_attach_hints_no_copy(ctx, message); stanza_attach_hints_no_store(ctx, message); -- cgit 1.4.1-2-gfad0 From d871efdcf91cce85e0dcc543d5be51b32871903f Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 28 Feb 2019 20:22:10 +0140 Subject: Add chat session state to OMEMO message stanza --- src/xmpp/message.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 5bad4980..a2911e0d 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -327,6 +327,7 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt) { + char *state = chat_session_get_state(barejid); xmpp_ctx_t * const ctx = connection_get_ctx(); char *id = connection_create_stanza_id("msg"); @@ -406,6 +407,10 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, xmpp_stanza_release(body_text); xmpp_stanza_add_child(message, body); + if (state) { + stanza_attach_state(ctx, message, state); + } + stanza_attach_carbons_private(ctx, message); stanza_attach_hints_no_copy(ctx, message); stanza_attach_hints_no_store(ctx, message); -- cgit 1.4.1-2-gfad0 From 810ea3222319e349bfc149fd85be470247b66c96 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 1 Mar 2019 05:10:23 +0220 Subject: Follow normal workflow for OMEMO message reception We try to decrypt all messages, if it's successful we use sv_ev_incoming_message even for OMEMO messages. We pass an OMEMO boolean to let UI be aware that message were encrypted. --- src/event/server_events.c | 2 +- src/event/server_events.h | 2 +- src/plugins/api.c | 2 +- src/xmpp/message.c | 143 +++++++++------------------------------------- src/xmpp/omemo.c | 79 +++++++++++++++++++++++++ src/xmpp/omemo.h | 1 + 6 files changed, 110 insertions(+), 119 deletions(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index 69883141..ccb57213 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -442,7 +442,7 @@ _sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, char *barejid, cha } void -sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp) +sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo) { gboolean new_win = FALSE; ProfChatWin *chatwin = wins_get_chat(barejid); diff --git a/src/event/server_events.h b/src/event/server_events.h index cc261487..b12ac94c 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -49,7 +49,7 @@ void sv_ev_room_history(const char *const room_jid, const char *const nick, GDateTime *timestamp, const char *const message); void sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message); -void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp); +void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo); void sv_ev_incoming_private_message(const char *const fulljid, char *message); void sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTime *timestamp); void sv_ev_typing(char *barejid, char *resource); diff --git a/src/plugins/api.c b/src/plugins/api.c index 4d8434e1..fc47f193 100644 --- a/src/plugins/api.c +++ b/src/plugins/api.c @@ -473,7 +473,7 @@ api_settings_int_set(const char *const group, const char *const key, int value) void api_incoming_message(const char *const barejid, const char *const resource, const char *const message) { - sv_ev_incoming_message((char*)barejid, (char*)resource, (char*)message, NULL, NULL); + sv_ev_incoming_message((char*)barejid, (char*)resource, (char*)message, NULL, NULL, FALSE); // TODO handle all states sv_ev_activity((char*)barejid, (char*)resource, FALSE); diff --git a/src/xmpp/message.c b/src/xmpp/message.c index a2911e0d..08ff4530 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -64,6 +64,7 @@ #include "xmpp/xmpp.h" #ifdef HAVE_OMEMO +#include "xmpp/omemo.h" #include "omemo/omemo.h" #endif @@ -82,7 +83,6 @@ static void _handle_conference(xmpp_stanza_t *const stanza); static void _handle_captcha(xmpp_stanza_t *const stanza); static void _handle_receipt_received(xmpp_stanza_t *const stanza); static void _handle_chat(xmpp_stanza_t *const stanza); -static void _handle_omemo(xmpp_stanza_t *const stanza); static void _send_message_stanza(xmpp_stanza_t *const stanza); @@ -150,13 +150,6 @@ _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *con } } -#ifdef HAVE_OMEMO - xmpp_stanza_t *omemo = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); - if (omemo) { - _handle_omemo(stanza); - } -#endif - _handle_chat(stanza); return 1; @@ -327,7 +320,7 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt) { - char *state = chat_session_get_state(barejid); + char *state = chat_session_get_state(jid); xmpp_ctx_t * const ctx = connection_get_ctx(); char *id = connection_create_stanza_id("msg"); @@ -932,6 +925,7 @@ _handle_carbons(xmpp_stanza_t *const stanza) static void _handle_chat(xmpp_stanza_t *const stanza) { + char *message = NULL; // ignore if type not chat or absent const char *type = xmpp_stanza_get_type(stanza); if (!(g_strcmp0(type, "chat") == 0 || type == NULL)) { @@ -944,11 +938,17 @@ _handle_chat(xmpp_stanza_t *const stanza) return; } + // check omemo encryption + gboolean omemo = FALSE; +#ifdef HAVE_OMEMO + message = omemo_receive_message(stanza); + omemo = message != NULL; +#endif + // ignore handled namespaces xmpp_stanza_t *conf = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CONFERENCE); xmpp_stanza_t *captcha = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CAPTCHA); - xmpp_stanza_t *omemo = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); - if (conf || captcha || omemo) { + if (conf || captcha) { return; } @@ -974,19 +974,24 @@ _handle_chat(xmpp_stanza_t *const stanza) // standard chat message, use jid without resource xmpp_ctx_t *ctx = connection_get_ctx(); GDateTime *timestamp = stanza_get_delay(stanza); - if (body) { - char *message = xmpp_stanza_get_text(body); - if (message) { - char *enc_message = NULL; - xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_ENCRYPTED); - if (x) { - enc_message = xmpp_stanza_get_text(x); - } - sv_ev_incoming_message(jid->barejid, jid->resourcepart, message, enc_message, timestamp); - xmpp_free(ctx, enc_message); + if (!message && body) { + message = xmpp_stanza_get_text(body); + } - _receipt_request_handler(stanza); + if (message) { + char *enc_message = NULL; + xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_ENCRYPTED); + if (x) { + enc_message = xmpp_stanza_get_text(x); + } + sv_ev_incoming_message(jid->barejid, jid->resourcepart, message, enc_message, timestamp, omemo); + xmpp_free(ctx, enc_message); + _receipt_request_handler(stanza); + + if (omemo) { + free(message); + } else { xmpp_free(ctx, message); } } @@ -1016,100 +1021,6 @@ _handle_chat(xmpp_stanza_t *const stanza) jid_destroy(jid); } -static void -_handle_omemo(xmpp_stanza_t *const stanza) -{ - xmpp_stanza_t *encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); - if (!encrypted) { - return; - } - - xmpp_stanza_t *header = xmpp_stanza_get_child_by_name(encrypted, "header"); - if (!header) { - return; - } - - const char *sid_text = xmpp_stanza_get_attribute(header, "sid"); - if (!sid_text) { - return; - } - uint32_t sid = strtoul(sid_text, NULL, 10); - - xmpp_stanza_t *iv = xmpp_stanza_get_child_by_name(header, "iv"); - if (!iv) { - return; - } - const char *iv_text = xmpp_stanza_get_text(iv); - if (!iv_text) { - return; - } - size_t iv_len; - const unsigned char *iv_raw = g_base64_decode(iv_text, &iv_len); - - xmpp_stanza_t *payload = xmpp_stanza_get_child_by_name(encrypted, "payload"); - if (!payload) { - return; - } - const char *payload_text = xmpp_stanza_get_text(payload); - if (!payload_text) { - return; - } - size_t payload_len; - const unsigned char *payload_raw = g_base64_decode(payload_text, &payload_len); - - GList *keys = NULL; - xmpp_stanza_t *key_stanza; - for (key_stanza = xmpp_stanza_get_children(header); key_stanza != NULL; key_stanza = xmpp_stanza_get_next(key_stanza)) { - if (g_strcmp0(xmpp_stanza_get_name(key_stanza), "key") != 0) { - continue; - } - - omemo_key_t *key = malloc(sizeof(omemo_key_t)); - const char *key_text = xmpp_stanza_get_text(key_stanza); - if (!key_text) { - goto skip; - } - - - const char *rid_text = xmpp_stanza_get_attribute(key_stanza, "rid"); - key->device_id = strtoul(rid_text, NULL, 10); - if (!key->device_id) { - goto skip; - } - key->data = g_base64_decode(key_text, &key->length); - key->prekey = g_strcmp0(xmpp_stanza_get_attribute(key_stanza, "prekey"), "true") == 0; - keys = g_list_append(keys, key); - continue; - -skip: - free(key); - } - - const char *from = xmpp_stanza_get_from(stanza); - Jid *jid = jid_create(from); - GDateTime *timestamp = stanza_get_delay(stanza); - - char *plaintext = omemo_on_message_recv(jid->barejid, sid, iv_raw, iv_len, keys, payload_raw, payload_len); - if (!plaintext) { - goto out; - } - - gboolean new_win = FALSE; - ProfChatWin *chatwin = wins_get_chat(jid->barejid); - if (!chatwin) { - ProfWin *window = wins_new_chat(jid->barejid); - chatwin = (ProfChatWin*)window; - new_win = TRUE; - } - - chat_log_omemo_msg_in(jid->barejid, plaintext, timestamp); - chatwin_incoming_msg(chatwin, jid->resourcepart, plaintext, timestamp, new_win, PROF_MSG_OMEMO); - -out: - jid_destroy(jid); - if (timestamp) g_date_time_unref(timestamp); -} - static void _send_message_stanza(xmpp_stanza_t *const stanza) { diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 048126fa..25057d55 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -191,6 +191,85 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons return 1; } +char * +omemo_receive_message(xmpp_stanza_t *const stanza) +{ + xmpp_stanza_t *encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); + if (!encrypted) { + return NULL; + } + + xmpp_stanza_t *header = xmpp_stanza_get_child_by_name(encrypted, "header"); + if (!header) { + return NULL; + } + + const char *sid_text = xmpp_stanza_get_attribute(header, "sid"); + if (!sid_text) { + return NULL; + } + uint32_t sid = strtoul(sid_text, NULL, 10); + + xmpp_stanza_t *iv = xmpp_stanza_get_child_by_name(header, "iv"); + if (!iv) { + return NULL; + } + const char *iv_text = xmpp_stanza_get_text(iv); + if (!iv_text) { + return NULL; + } + size_t iv_len; + const unsigned char *iv_raw = g_base64_decode(iv_text, &iv_len); + + xmpp_stanza_t *payload = xmpp_stanza_get_child_by_name(encrypted, "payload"); + if (!payload) { + return NULL; + } + const char *payload_text = xmpp_stanza_get_text(payload); + if (!payload_text) { + return NULL; + } + size_t payload_len; + const unsigned char *payload_raw = g_base64_decode(payload_text, &payload_len); + + GList *keys = NULL; + xmpp_stanza_t *key_stanza; + for (key_stanza = xmpp_stanza_get_children(header); key_stanza != NULL; key_stanza = xmpp_stanza_get_next(key_stanza)) { + if (g_strcmp0(xmpp_stanza_get_name(key_stanza), "key") != 0) { + continue; + } + + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + const char *key_text = xmpp_stanza_get_text(key_stanza); + if (!key_text) { + goto skip; + } + + + const char *rid_text = xmpp_stanza_get_attribute(key_stanza, "rid"); + key->device_id = strtoul(rid_text, NULL, 10); + if (!key->device_id) { + goto skip; + } + key->data = g_base64_decode(key_text, &key->length); + key->prekey = g_strcmp0(xmpp_stanza_get_attribute(key_stanza, "prekey"), "true") == 0; + keys = g_list_append(keys, key); + continue; + +skip: + free(key); + } + + const char *from = xmpp_stanza_get_from(stanza); + Jid *jid = jid_create(from); + + char *plaintext = omemo_on_message_recv(jid->barejid, sid, iv_raw, iv_len, keys, payload_raw, payload_len); + + jid_destroy(jid); + + return plaintext; +} + static int _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) { diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index c384c4f0..81c3b979 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -8,3 +8,4 @@ void omemo_devicelist_request(const char * const jid); void omemo_bundle_publish(void); 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); -- cgit 1.4.1-2-gfad0 From 421d1b15608d21e693c97b77cacf6e2db74d5abd Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 1 Mar 2019 19:21:20 +0140 Subject: Add OMEMO identity materials long term storage --- src/config/files.h | 1 + src/event/server_events.c | 8 ++++ src/omemo/omemo.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-- src/omemo/omemo.h | 1 + src/omemo/store.h | 6 +++ 5 files changed, 109 insertions(+), 4 deletions(-) diff --git a/src/config/files.h b/src/config/files.h index 1d8d2890..f7dfa29a 100644 --- a/src/config/files.h +++ b/src/config/files.h @@ -50,6 +50,7 @@ #define DIR_CHATLOGS "chatlogs" #define DIR_OTR "otr" #define DIR_PGP "pgp" +#define DIR_OMEMO "omemo" #define DIR_PLUGINS "plugins" void files_create_directories(void); diff --git a/src/event/server_events.c b/src/event/server_events.c index ccb57213..b8ee36cf 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -59,6 +59,10 @@ #include "pgp/gpg.h" #endif +#ifdef HAVE_OMEMO +#include "omemo/omemo.h" +#endif + #include "ui/ui.h" void @@ -76,6 +80,10 @@ sv_ev_login_account_success(char *account_name, gboolean secured) p_gpg_on_connect(account->jid); #endif +#ifdef HAVE_OMEMO + omemo_on_connect(account); +#endif + ui_handle_login_account_success(account, secured); // attempt to rejoin rooms with passwords diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index a9860851..8b052d4e 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1,5 +1,7 @@ #include +#include +#include #include #include #include @@ -18,9 +20,11 @@ #include "xmpp/xmpp.h" #include "xmpp/connection.h" #include "xmpp/omemo.h" +#include "config/files.h" static gboolean loaded; +static void omemo_generate_short_term_crypto_materials(ProfAccount *account); static void lock(void *user_data); static void unlock(void *user_data); static void omemo_log(int level, const char *message, size_t len, void *user_data); @@ -41,6 +45,8 @@ struct omemo_context_t { GHashTable *signed_pre_key_store; identity_key_store_t identity_key_store; GHashTable *device_ids; + GString *identity_filename; + GKeyFile *identity_keyfile; }; static omemo_context omemo_ctx; @@ -142,15 +148,98 @@ omemo_init(void) } void -omemo_generate_crypto_materials(ProfAccount *account) +omemo_on_connect(ProfAccount *account) { xmpp_ctx_t * const ctx = connection_get_ctx(); char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + char *omemodir = files_get_data_path(DIR_OMEMO); + GString *basedir = g_string_new(omemodir); + free(omemodir); + gchar *account_dir = str_replace(barejid, "@", "_at_"); + g_string_append(basedir, "/"); + g_string_append(basedir, account_dir); + g_string_append(basedir, "/"); + free(account_dir); + + omemo_ctx.identity_filename = g_string_new(basedir->str); + g_string_append(omemo_ctx.identity_filename, "identity.txt"); + + errno = 0; + int res = g_mkdir_with_parents(basedir->str, S_IRWXU); + if (res == -1) { + char *errmsg = strerror(errno); + if (errmsg) { + log_error("Error creating directory: %s, %s", omemo_ctx.identity_filename->str, errmsg); + } else { + log_error("Error creating directory: %s", omemo_ctx.identity_filename->str); + } + } + + omemo_ctx.identity_keyfile = g_key_file_new(); + if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + omemo_ctx.device_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, NULL); + omemo_ctx.registration_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, NULL); + + char *identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, NULL); + size_t identity_key_public_len; + unsigned char *identity_key_public = g_base64_decode(identity_key_public_b64, &identity_key_public_len); + omemo_ctx.identity_key_store.public = signal_buffer_create(identity_key_public, identity_key_public_len); + + char *identity_key_private_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, NULL); + size_t identity_key_private_len; + unsigned char *identity_key_private = g_base64_decode(identity_key_private_b64, &identity_key_private_len); + omemo_ctx.identity_key_store.private = signal_buffer_create(identity_key_private, identity_key_private_len); + signal_buffer_create(identity_key_private, identity_key_private_len); + + ec_public_key *public_key; + curve_decode_point(&public_key, identity_key_public, identity_key_public_len, omemo_ctx.signal); + ec_private_key *private_key; + curve_decode_private_point(&private_key, identity_key_private, identity_key_private_len, omemo_ctx.signal); + ratchet_identity_key_pair_create(&omemo_ctx.identity_key_pair, public_key, private_key); + + g_free(identity_key_public); + g_free(identity_key_private); + + omemo_generate_short_term_crypto_materials(account); + } +} + +void +omemo_generate_crypto_materials(ProfAccount *account) +{ + GError *error = NULL; + + if (loaded) { + return; + } omemo_ctx.device_id = randombytes_uniform(0x80000000); signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); + + ec_public_key_serialize(&omemo_ctx.identity_key_store.public, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair)); + ec_private_key_serialize(&omemo_ctx.identity_key_store.private, ratchet_identity_key_pair_get_private(omemo_ctx.identity_key_pair)); + + g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, omemo_ctx.device_id); + g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, omemo_ctx.registration_id); + char *identity_key_public = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.public), signal_buffer_len(omemo_ctx.identity_key_store.public)); + g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, identity_key_public); + g_free(identity_key_public); + char *identity_key_private = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.private), signal_buffer_len(omemo_ctx.identity_key_store.private)); + g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, identity_key_private); + g_free(identity_key_private); + + if (!g_key_file_save_to_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, &error)) { + log_error("Error saving OMEMO identity to: %s, %s", omemo_ctx.identity_filename->str, error->message); + } + + omemo_generate_short_term_crypto_materials(account); +} + +static void +omemo_generate_short_term_crypto_materials(ProfAccount *account) +{ signal_protocol_key_helper_generate_pre_keys(&omemo_ctx.pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); struct timeval tv; @@ -158,14 +247,14 @@ omemo_generate_crypto_materials(ProfAccount *account) unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; signal_protocol_key_helper_generate_signed_pre_key(&omemo_ctx.signed_pre_key, omemo_ctx.identity_key_pair, 5, timestamp, omemo_ctx.signal); - ec_public_key_serialize(&omemo_ctx.identity_key_store.public, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair)); - ec_private_key_serialize(&omemo_ctx.identity_key_store.private, ratchet_identity_key_pair_get_private(omemo_ctx.identity_key_pair)); - loaded = TRUE; /* Ensure we get our current device list, and it gets updated with our * device_id */ + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); omemo_devicelist_request(barejid); + omemo_bundle_publish(); } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index eb9569a3..cffc63f1 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -15,6 +15,7 @@ typedef struct omemo_key { } omemo_key_t; void omemo_init(void); +void omemo_on_connect(ProfAccount *account); void omemo_generate_crypto_materials(ProfAccount *account); uint32_t omemo_device_id(void); diff --git a/src/omemo/store.h b/src/omemo/store.h index e99c514b..491e1495 100644 --- a/src/omemo/store.h +++ b/src/omemo/store.h @@ -1,5 +1,11 @@ #include +#define OMEMO_STORE_GROUP_IDENTITY "identity" +#define OMEMO_STORE_KEY_DEVICE_ID "device_id" +#define OMEMO_STORE_KEY_REGISTRATION_ID "registration_id" +#define OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC "identity_key_public" +#define OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE "identity_key_private" + typedef struct { signal_buffer *public; signal_buffer *private; -- cgit 1.4.1-2-gfad0 From 20ed86c58c3ae9ad23b27010ec4e429661b8c311 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 4 Mar 2019 07:38:32 +0140 Subject: Handle requested device list for session start --- src/omemo/omemo.c | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 8b052d4e..4ca8bd47 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -28,6 +28,10 @@ static void omemo_generate_short_term_crypto_materials(ProfAccount *account); static void lock(void *user_data); static void unlock(void *user_data); static void omemo_log(int level, const char *message, size_t len, void *user_data); +static gboolean handle_own_device_list(const char *const jid, GList *device_list); +static gboolean handle_device_list_start_session(const char *const jid, GList *device_list); + +typedef gboolean (*OmemoDeviceListHandler)(const char *const jid, GList *device_list); struct omemo_context_t { pthread_mutexattr_t attr; @@ -35,6 +39,7 @@ struct omemo_context_t { signal_context *signal; uint32_t device_id; GHashTable *device_list; + GHashTable *device_list_handler; ratchet_identity_key_pair *identity_key_pair; uint32_t registration_id; signal_protocol_key_helper_pre_key_list_node *pre_keys_head; @@ -145,6 +150,7 @@ omemo_init(void) loaded = 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); } void @@ -253,7 +259,9 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) * device_id */ xmpp_ctx_t * const ctx = connection_get_ctx(); char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), handle_own_device_list); omemo_devicelist_request(barejid); + free(barejid); omemo_bundle_publish(); } @@ -264,7 +272,7 @@ omemo_start_session(const char *const barejid) GList *device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid); if (!device_list) { omemo_devicelist_request(barejid); - /* TODO handle response */ + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), handle_device_list_start_session); return; } @@ -338,16 +346,19 @@ void omemo_set_device_list(const char *const jid, GList * device_list) { xmpp_ctx_t * const ctx = connection_get_ctx(); - char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + char *barejid = xmpp_jid_bare(ctx, jid); - if (g_strcmp0(jid, barejid) == 0) { - if (!g_list_find(device_list, GINT_TO_POINTER(omemo_ctx.device_id))) { - device_list = g_list_append(device_list, GINT_TO_POINTER(omemo_ctx.device_id)); - omemo_devicelist_publish(device_list); + g_hash_table_insert(omemo_ctx.device_list, strdup(barejid), device_list); + + OmemoDeviceListHandler handler = g_hash_table_lookup(omemo_ctx.device_list_handler, barejid); + if (handler) { + gboolean keep = handler(barejid, device_list); + if (!keep) { + g_hash_table_remove(omemo_ctx.device_list_handler, barejid); } } - g_hash_table_insert(omemo_ctx.device_list, strdup(jid), device_list); + free(barejid); } void @@ -535,3 +546,26 @@ omemo_log(int level, const char *message, size_t len, void *user_data) { cons_show(message); } + +static gboolean +handle_own_device_list(const char *const jid, GList *device_list) +{ + if (!g_list_find(device_list, GINT_TO_POINTER(omemo_ctx.device_id))) { + gpointer original_jid; + g_hash_table_steal_extended(omemo_ctx.device_list, jid, &original_jid, NULL); + free(original_jid); + device_list = g_list_append(device_list, GINT_TO_POINTER(omemo_ctx.device_id)); + g_hash_table_insert(omemo_ctx.device_list, strdup(jid), device_list); + omemo_devicelist_publish(device_list); + } + + return TRUE; +} + +static gboolean +handle_device_list_start_session(const char *const jid, GList *device_list) +{ + omemo_start_session(jid); + + return FALSE; +} -- cgit 1.4.1-2-gfad0 From 9b8c1d7d2c3c5ecc8abd431b9d407a59c95446f9 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 5 Mar 2019 20:01:15 +0140 Subject: Add support for encrypted carbon Fix {signed,}_pre_key store --- src/event/server_events.c | 2 +- src/event/server_events.h | 2 +- src/omemo/omemo.c | 103 ++++++++++++++++++------ src/omemo/store.c | 44 ++++++++--- src/omemo/store.h | 198 ++++++++++++++++++++++++++++++++++++++++++++++ src/xmpp/message.c | 18 ++++- src/xmpp/stanza.c | 2 +- 7 files changed, 326 insertions(+), 43 deletions(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index b8ee36cf..33712328 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -510,7 +510,7 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m } void -sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message) +sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message, gboolean omemo) { gboolean new_win = FALSE; ProfChatWin *chatwin = wins_get_chat(barejid); diff --git a/src/event/server_events.h b/src/event/server_events.h index b12ac94c..96fdb58c 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -74,7 +74,7 @@ void sv_ev_room_banned(const char *const room, const char *const actor, const ch void sv_ev_room_occupent_banned(const char *const room, const char *const nick, const char *const actor, const char *const reason); void sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message); -void sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message); +void sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message, gboolean omemo); void sv_ev_xmpp_stanza(const char *const msg); void sv_ev_muc_self_online(const char *const room, const char *const nick, gboolean config_required, const char *const role, const char *const affiliation, const char *const actor, const char *const reason, diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 4ca8bd47..6e964e81 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -42,8 +42,7 @@ struct omemo_context_t { GHashTable *device_list_handler; ratchet_identity_key_pair *identity_key_pair; uint32_t registration_id; - signal_protocol_key_helper_pre_key_list_node *pre_keys_head; - session_signed_pre_key *signed_pre_key; + uint32_t signed_pre_key_id; signal_protocol_store_context *store; GHashTable *session_store; GHashTable *pre_key_store; @@ -132,7 +131,7 @@ omemo_init(void) .contains_signed_pre_key = contains_signed_pre_key, .remove_signed_pre_key = remove_signed_pre_key, .destroy_func = NULL, - .user_data = omemo_ctx.pre_key_store + .user_data = omemo_ctx.signed_pre_key_store }; signal_protocol_store_context_set_signed_pre_key_store(omemo_ctx.store, &signed_pre_key_store); @@ -246,12 +245,23 @@ omemo_generate_crypto_materials(ProfAccount *account) static void omemo_generate_short_term_crypto_materials(ProfAccount *account) { - signal_protocol_key_helper_generate_pre_keys(&omemo_ctx.pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); + signal_protocol_key_helper_pre_key_list_node *pre_keys_head; + signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); + session_signed_pre_key *signed_pre_key; struct timeval tv; gettimeofday(&tv, NULL); unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; - signal_protocol_key_helper_generate_signed_pre_key(&omemo_ctx.signed_pre_key, omemo_ctx.identity_key_pair, 5, timestamp, omemo_ctx.signal); + + omemo_ctx.signed_pre_key_id = 1; + signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, omemo_ctx.identity_key_pair, omemo_ctx.signed_pre_key_id, timestamp, omemo_ctx.signal); + + signal_protocol_key_helper_pre_key_list_node *p; + for (p = pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { + session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); + signal_protocol_pre_key_store_key(omemo_ctx.store, prekey); + } + signal_protocol_signed_pre_key_store_key(omemo_ctx.store, signed_pre_key); loaded = TRUE; @@ -308,8 +318,11 @@ omemo_identity_key(unsigned char **output, size_t *length) void omemo_signed_prekey(unsigned char **output, size_t *length) { + session_signed_pre_key *signed_pre_key; signal_buffer *buffer = NULL; - ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(omemo_ctx.signed_pre_key))); + + signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id); + ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(signed_pre_key))); *length = signal_buffer_len(buffer); *output = malloc(*length); memcpy(*output, signal_buffer_data(buffer), *length); @@ -319,25 +332,36 @@ omemo_signed_prekey(unsigned char **output, size_t *length) void omemo_signed_prekey_signature(unsigned char **output, size_t *length) { - *length = session_signed_pre_key_get_signature_len(omemo_ctx.signed_pre_key); + session_signed_pre_key *signed_pre_key; + + signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id); + *length = session_signed_pre_key_get_signature_len(signed_pre_key); *output = malloc(*length); - memcpy(*output, session_signed_pre_key_get_signature(omemo_ctx.signed_pre_key), *length); + memcpy(*output, session_signed_pre_key_get_signature(signed_pre_key), *length); } void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths) { - signal_protocol_key_helper_pre_key_list_node *p; - for (p = omemo_ctx.pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { - session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); - signal_buffer *buffer = NULL; - ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_pre_key_get_key_pair(prekey))); - size_t length = signal_buffer_len(buffer); + GHashTableIter iter; + gpointer id; + + g_hash_table_iter_init(&iter, omemo_ctx.pre_key_store); + while (g_hash_table_iter_next(&iter, &id, NULL)) { + session_pre_key *pre_key; + int ret; + ret = signal_protocol_pre_key_load_key(omemo_ctx.store, &pre_key, GPOINTER_TO_INT(id)); + if (ret != SG_SUCCESS) { + continue; + } + + signal_buffer *public_key; + ec_public_key_serialize(&public_key, ec_key_pair_get_public(session_pre_key_get_key_pair(pre_key))); + size_t length = signal_buffer_len(public_key); unsigned char *prekey_value = malloc(length); - memcpy(prekey_value, signal_buffer_data(buffer), length); - signal_buffer_free(buffer); + memcpy(prekey_value, signal_buffer_data(public_key), length); *prekeys = g_list_append(*prekeys, prekey_value); - *ids = g_list_append(*ids, GINT_TO_POINTER(session_pre_key_get_id(prekey))); + *ids = g_list_append(*ids, GINT_TO_POINTER(id)); *lengths = g_list_append(*lengths, GINT_TO_POINTER(length)); } } @@ -349,6 +373,10 @@ omemo_set_device_list(const char *const jid, GList * device_list) char *barejid = xmpp_jid_bare(ctx, jid); g_hash_table_insert(omemo_ctx.device_list, strdup(barejid), device_list); + if (strchr(barejid, '@') == NULL) { + // barejid is server so we should handle it as our own device list + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), handle_own_device_list); + } OmemoDeviceListHandler handler = g_hash_table_lookup(omemo_ctx.device_list_handler, barejid); if (handler) { @@ -397,16 +425,13 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean int res; xmpp_ctx_t * const ctx = connection_get_ctx(); char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); - GList *device_ids = NULL; GList *recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, chatwin->barejid); if (!recipient_device_id) { return FALSE; } - device_ids = g_list_copy(recipient_device_id); GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, barejid); - device_ids = g_list_concat(device_ids, g_list_copy(sender_device_id)); /* TODO generate fresh AES-GCM materials */ /* TODO encrypt message */ @@ -430,7 +455,7 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean GList *keys = NULL; GList *device_ids_iter; - for (device_ids_iter = device_ids; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { + for (device_ids_iter = recipient_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { int res; ciphertext_message *ciphertext; session_cipher *cipher; @@ -452,7 +477,33 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean key->data = signal_buffer_data(buffer); key->length = signal_buffer_len(buffer); key->device_id = GPOINTER_TO_INT(device_ids_iter->data); - key->prekey = TRUE; + key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; + keys = g_list_append(keys, key); + } + + for (device_ids_iter = sender_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { + int res; + ciphertext_message *ciphertext; + session_cipher *cipher; + signal_protocol_address address = { + barejid, strlen(barejid), GPOINTER_TO_INT(device_ids_iter->data) + }; + + res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); + if (res != 0) { + continue; + } + + res = session_cipher_encrypt(cipher, key, AES128_GCM_KEY_LENGTH, &ciphertext); + if (res != 0) { + continue; + } + signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + key->data = signal_buffer_data(buffer); + key->length = signal_buffer_len(buffer); + key->device_id = GPOINTER_TO_INT(device_ids_iter->data); + key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; keys = g_list_append(keys, key); } @@ -465,7 +516,6 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean free(ciphertext); sodium_free(key); sodium_free(iv); - g_list_free(device_ids); return TRUE; } @@ -544,7 +594,7 @@ unlock(void *user_data) static void omemo_log(int level, const char *message, size_t len, void *user_data) { - cons_show(message); + cons_show("OMEMO: %s", message); } static gboolean @@ -559,6 +609,11 @@ handle_own_device_list(const char *const jid, GList *device_list) omemo_devicelist_publish(device_list); } + GList *device_id; + for (device_id = device_list; device_id != NULL; device_id = device_id->next) { + omemo_bundle_request(jid, GPOINTER_TO_INT(device_id->data), omemo_start_device_session_handle_bundle, free, strdup(jid)); + } + return TRUE; } diff --git a/src/omemo/store.c b/src/omemo/store.c index ab8cd81b..9fec33e5 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -96,9 +96,19 @@ store_session(const signal_protocol_address *address, uint8_t *record, int contains_session(const signal_protocol_address *address, void *user_data) { - signal_buffer *record; - load_session(&record, address, user_data); - return record != NULL; + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, address->name); + if (!device_store) { + return 0; + } + + if (!g_hash_table_lookup(device_store, GINT_TO_POINTER(address->device_id))) { + return 0; + } + + return 1; } int @@ -134,9 +144,15 @@ delete_all_sessions(const char *name, size_t name_len, void *user_data) int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data) { + signal_buffer *original; GHashTable *pre_key_store = (GHashTable *)user_data; - *record = g_hash_table_lookup(pre_key_store, GINT_TO_POINTER(pre_key_id)); + original = g_hash_table_lookup(pre_key_store, GINT_TO_POINTER(pre_key_id)); + if (original == NULL) { + return SG_ERR_INVALID_KEY_ID; + } + + *record = signal_buffer_copy(original); return SG_SUCCESS; } @@ -154,10 +170,9 @@ store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, int contains_pre_key(uint32_t pre_key_id, void *user_data) { - signal_buffer *record; - load_pre_key(&record, pre_key_id, user_data); + GHashTable *pre_key_store = (GHashTable *)user_data; - return record != NULL; + return g_hash_table_lookup(pre_key_store, GINT_TO_POINTER(pre_key_id)) != NULL; } int @@ -172,9 +187,15 @@ int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data) { + signal_buffer *original; GHashTable *signed_pre_key_store = (GHashTable *)user_data; - *record = g_hash_table_lookup(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); + original = g_hash_table_lookup(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); + if (!original) { + return SG_ERR_INVALID_KEY_ID; + } + + *record = signal_buffer_copy(original); return SG_SUCCESS; } @@ -192,10 +213,9 @@ store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) { - signal_buffer *record; - load_signed_pre_key(&record, signed_pre_key_id, user_data); + GHashTable *signed_pre_key_store = (GHashTable *)user_data; - return record != NULL; + return g_hash_table_lookup(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)) != NULL; } int @@ -251,7 +271,7 @@ is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, signal_buffer *buffer = signal_buffer_create(key_data, key_len); signal_buffer *original = g_hash_table_lookup(identity_key_store->identity_key_store, node); - return original == NULL || signal_buffer_compare(buffer, original); + return original == NULL || signal_buffer_compare(buffer, original) == 0; } int diff --git a/src/omemo/store.h b/src/omemo/store.h index 491e1495..ad33d5a5 100644 --- a/src/omemo/store.h +++ b/src/omemo/store.h @@ -18,23 +18,221 @@ GHashTable * pre_key_store_new(void); GHashTable * signed_pre_key_store_new(void); void identity_key_store_new(identity_key_store_t *identity_key_store); +/** + * Returns a copy of the serialized session record corresponding to the + * provided recipient ID + device ID tuple. + * + * @param record pointer to a freshly allocated buffer containing the + * serialized session record. Unset if no record was found. + * The Signal Protocol library is responsible for freeing this buffer. + * @param address the address of the remote client + * @return 1 if the session was loaded, 0 if the session was not found, negative on failure + */ int load_session(signal_buffer **record, const signal_protocol_address *address, void *user_data); + +/** + * Returns all known devices with active sessions for a recipient + * + * @param pointer to an array that will be allocated and populated with the result + * @param name the name of the remote client + * @param name_len the length of the name + * @return size of the sessions array, or negative on failure + */ int get_sub_device_sessions(signal_int_list **sessions, const char *name, size_t name_len, void *user_data); + +/** + * Commit to storage the session record for a given + * recipient ID + device ID tuple. + * + * @param address the address of the remote client + * @param record pointer to a buffer containing the serialized session + * record for the remote client + * @param record_len length of the serialized session record + * @return 0 on success, negative on failure + */ int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, void *user_data); + +/** + * Determine whether there is a committed session record for a + * recipient ID + device ID tuple. + * + * @param address the address of the remote client + * @return 1 if a session record exists, 0 otherwise. + */ int contains_session(const signal_protocol_address *address, void *user_data); + +/** + * Remove a session record for a recipient ID + device ID tuple. + * + * @param address the address of the remote client + * @return 1 if a session was deleted, 0 if a session was not deleted, negative on error + */ int delete_session(const signal_protocol_address *address, void *user_data); + +/** + * Remove the session records corresponding to all devices of a recipient ID. + * + * @param name the name of the remote client + * @param name_len the length of the name + * @return the number of deleted sessions on success, negative on failure + */ int delete_all_sessions(const char *name, size_t name_len, void *user_data); + +/** + * Load a local serialized PreKey record. + * + * @param record pointer to a newly allocated buffer containing the record, + * if found. Unset if no record was found. + * The Signal Protocol library is responsible for freeing this buffer. + * @param pre_key_id the ID of the local serialized PreKey record + * @retval SG_SUCCESS if the key was found + * @retval SG_ERR_INVALID_KEY_ID if the key could not be found + */ int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data); + +/** + * Store a local serialized PreKey record. + * + * @param pre_key_id the ID of the PreKey record to store. + * @param record pointer to a buffer containing the serialized record + * @param record_len length of the serialized record + * @return 0 on success, negative on failure + */ int store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data); + +/** + * Determine whether there is a committed PreKey record matching the + * provided ID. + * + * @param pre_key_id A PreKey record ID. + * @return 1 if the store has a record for the PreKey ID, 0 otherwise + */ int contains_pre_key(uint32_t pre_key_id, void *user_data); + +/** + * Delete a PreKey record from local storage. + * + * @param pre_key_id The ID of the PreKey record to remove. + * @return 0 on success, negative on failure + */ int remove_pre_key(uint32_t pre_key_id, void *user_data); + +/** + * Load a local serialized signed PreKey record. + * + * @param record pointer to a newly allocated buffer containing the record, + * if found. Unset if no record was found. + * The Signal Protocol library is responsible for freeing this buffer. + * @param signed_pre_key_id the ID of the local signed PreKey record + * @retval SG_SUCCESS if the key was found + * @retval SG_ERR_INVALID_KEY_ID if the key could not be found + */ int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data); + +/** + * Store a local serialized signed PreKey record. + * + * @param signed_pre_key_id the ID of the signed PreKey record to store + * @param record pointer to a buffer containing the serialized record + * @param record_len length of the serialized record + * @return 0 on success, negative on failure + */ int store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data); + +/** + * Determine whether there is a committed signed PreKey record matching + * the provided ID. + * + * @param signed_pre_key_id A signed PreKey record ID. + * @return 1 if the store has a record for the signed PreKey ID, 0 otherwise + */ int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data); + +/** + * Delete a SignedPreKeyRecord from local storage. + * + * @param signed_pre_key_id The ID of the signed PreKey record to remove. + * @return 0 on success, negative on failure + */ int remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data); + +/** + * Get the local client's identity key pair. + * + * @param public_data pointer to a newly allocated buffer containing the + * public key, if found. Unset if no record was found. + * The Signal Protocol library is responsible for freeing this buffer. + * @param private_data pointer to a newly allocated buffer containing the + * private key, if found. Unset if no record was found. + * The Signal Protocol library is responsible for freeing this buffer. + * @return 0 on success, negative on failure + */ int get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data); + +/** + * Return the local client's registration ID. + * + * Clients should maintain a registration ID, a random number + * between 1 and 16380 that's generated once at install time. + * + * @param registration_id pointer to be set to the local client's + * registration ID, if it was successfully retrieved. + * @return 0 on success, negative on failure + */ int get_local_registration_id(void *user_data, uint32_t *registration_id); + +/** + * Save a remote client's identity key + *

+ * Store a remote client's identity key as trusted. + * The value of key_data may be null. In this case remove the key data + * from the identity store, but retain any metadata that may be kept + * alongside it. + * + * @param address the address of the remote client + * @param key_data Pointer to the remote client's identity key, may be null + * @param key_len Length of the remote client's identity key + * @return 0 on success, negative on failure + */ int save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data); + +/** + * Verify a remote client's identity key. + * + * Determine whether a remote client's identity is trusted. Convention is + * that the TextSecure protocol is 'trust on first use.' This means that + * an identity key is considered 'trusted' if there is no entry for the recipient + * in the local store, or if it matches the saved key for a recipient in the local + * store. Only if it mismatches an entry in the local store is it considered + * 'untrusted.' + * + * @param address the address of the remote client + * @param identityKey The identity key to verify. + * @param key_data Pointer to the identity key to verify + * @param key_len Length of the identity key to verify + * @return 1 if trusted, 0 if untrusted, negative on failure + */ int is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data); + +/** + * Store a serialized sender key record for a given + * (groupId + senderId + deviceId) tuple. + * + * @param sender_key_name the (groupId + senderId + deviceId) tuple + * @param record pointer to a buffer containing the serialized record + * @param record_len length of the serialized record + * @return 0 on success, negative on failure + */ int store_sender_key(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data); + +/** + * Returns a copy of the sender key record corresponding to the + * (groupId + senderId + deviceId) tuple. + * + * @param record pointer to a newly allocated buffer containing the record, + * if found. Unset if no record was found. + * The Signal Protocol library is responsible for freeing this buffer. + * @param sender_key_name the (groupId + senderId + deviceId) tuple + * @return 1 if the record was loaded, 0 if the record was not found, negative on failure + */ int load_sender_key(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name, void *user_data); diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 08ff4530..28fe96d8 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -840,6 +840,7 @@ _private_chat_handler(xmpp_stanza_t *const stanza, const char *const fulljid) static gboolean _handle_carbons(xmpp_stanza_t *const stanza) { + char *message_txt; xmpp_stanza_t *carbons = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CARBONS); if (!carbons) { return FALSE; @@ -873,10 +874,19 @@ _handle_carbons(xmpp_stanza_t *const stanza) return TRUE; } - char *message_txt = xmpp_message_get_body(message); + // check omemo encryption + gboolean omemo = FALSE; +#ifdef HAVE_OMEMO + message_txt = omemo_receive_message(message); + omemo = message_txt != NULL; +#endif + if (!message_txt) { - log_warning("Carbon received with no message."); - return TRUE; + message_txt = xmpp_message_get_body(message); + if (!message_txt) { + log_warning("Carbon received with no message."); + return TRUE; + } } Jid *my_jid = jid_create(connection_get_fulljid()); @@ -904,7 +914,7 @@ _handle_carbons(xmpp_stanza_t *const stanza) // if we are the recipient, treat as standard incoming message if (g_strcmp0(my_jid->barejid, jid_to->barejid) == 0) { - sv_ev_incoming_carbon(jid_from->barejid, jid_from->resourcepart, message_txt, enc_message); + sv_ev_incoming_carbon(jid_from->barejid, jid_from->resourcepart, message_txt, enc_message, omemo); // else treat as a sent message } else { diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 302258ec..61086f77 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2251,7 +2251,7 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, xmpp_stanza_t *prekey = xmpp_stanza_new(ctx); xmpp_stanza_set_name(prekey, "preKeyPublic"); char *id = g_strdup_printf("%d", GPOINTER_TO_INT(i->data)); - xmpp_stanza_set_attribute(prekey, "id", id); + xmpp_stanza_set_attribute(prekey, "preKeyId", id); g_free(id); xmpp_stanza_t *prekey_text = xmpp_stanza_new(ctx); -- cgit 1.4.1-2-gfad0 From bcd582be42c34b0ae41c81961b84a33bda33f997 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 5 Mar 2019 20:12:04 +0140 Subject: Don't explicitely disable carbons for OMEMO messages --- src/xmpp/message.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 28fe96d8..a1092235 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -404,7 +404,6 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, stanza_attach_state(ctx, message, state); } - stanza_attach_carbons_private(ctx, message); stanza_attach_hints_no_copy(ctx, message); stanza_attach_hints_no_store(ctx, message); -- cgit 1.4.1-2-gfad0 From 3d8c928e53fd8fecab44448e0d1b64ce4ed1a63c Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 5 Mar 2019 20:14:02 +0140 Subject: Use profanity logs for OMEMO logs --- src/omemo/omemo.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 6e964e81..98b6f24b 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -594,7 +594,21 @@ unlock(void *user_data) static void omemo_log(int level, const char *message, size_t len, void *user_data) { - cons_show("OMEMO: %s", message); + switch (level) { + case SG_LOG_ERROR: + log_error("OMEMO: %s", message); + break; + case SG_LOG_WARNING: + log_warning("OMEMO: %s", message); + break; + case SG_LOG_NOTICE: + case SG_LOG_INFO: + log_info("OMEMO: %s", message); + break; + case SG_LOG_DEBUG: + log_debug("OMEMO: %s", message); + break; + } } static gboolean -- cgit 1.4.1-2-gfad0 From 1309405d28eb71e66b1aead45254e0fd1ec81637 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 5 Mar 2019 20:19:46 +0140 Subject: Add log on OMEMO session start --- src/omemo/omemo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 98b6f24b..905c5c22 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -279,6 +279,7 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) void omemo_start_session(const char *const barejid) { + log_info("Start OMEMO session with %s", barejid); GList *device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid); if (!device_list) { omemo_devicelist_request(barejid); @@ -397,6 +398,7 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, size_t signature_len, const unsigned char *const identity_key_raw, size_t identity_key_len) { + log_info("Start OMEMO session with %s device %d", jid, device_id); session_pre_key_bundle *bundle; signal_protocol_address *address; -- cgit 1.4.1-2-gfad0 From 695694051e2f56d947ea8baf9199c1ff7bf81121 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 07:23:56 +0140 Subject: Remove hints preventing carbons to work properly --- src/xmpp/message.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index a1092235..d078e1e7 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -404,9 +404,6 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, stanza_attach_state(ctx, message, state); } - stanza_attach_hints_no_copy(ctx, message); - stanza_attach_hints_no_store(ctx, message); - if (request_receipt) { stanza_attach_receipt_request(ctx, message); } -- cgit 1.4.1-2-gfad0 From 605e06411ca6d184baf19157620d0473d7c3d557 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 18:57:11 +0140 Subject: Get rid of libsodium --- configure.ac | 8 ----- src/command/cmd_funcs.c | 2 +- src/omemo/crypto.c | 84 +++++++++++++++++++++++++++++++++++-------------- src/omemo/omemo.c | 20 ++++++------ src/omemo/omemo.h | 1 + 5 files changed, 73 insertions(+), 42 deletions(-) diff --git a/configure.ac b/configure.ac index cc53254f..edf72d60 100644 --- a/configure.ac +++ b/configure.ac @@ -276,14 +276,6 @@ if test "x$enable_omemo" != xno; then [AC_MSG_ERROR([libsignal-protocol-c is required for omemo support])], [AC_MSG_NOTICE([libsignal-protocol-c not found, omemo support not enabled])])]) - AC_CHECK_LIB([sodium], [sodium_init], - [AM_CONDITIONAL([BUILD_OMEMO], [true]) - AC_DEFINE([HAVE_OMEMO], [1], [Have omemo]), - LIBS="-lsodium $LIBS"], - [AS_IF([test "x$enable_omemo" = xyes], - [AC_MSG_ERROR([libsodium is required for omemo support])], - [AC_MSG_NOTICE([libsodium not found, omemo support not enabled])])]) - AC_CHECK_LIB([gcrypt], [gcry_check_version], [AM_CONDITIONAL([BUILD_OMEMO], [true]) AC_DEFINE([HAVE_OMEMO], [1], [Have omemo]), diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index de3372cd..1ce034d9 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7944,7 +7944,7 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) } if (!omemo_loaded()) { - win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo init'"); + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); return TRUE; } diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index 73b2ba0d..5119443a 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -1,19 +1,15 @@ #include #include #include -#include #include +#include "log.h" #include "omemo/omemo.h" #include "omemo/crypto.h" int omemo_crypto_init(void) { - if (sodium_init() < 0) { - return -1; - } - if (!gcry_check_version(GCRYPT_VERSION)) { return -1; } @@ -26,74 +22,114 @@ omemo_crypto_init(void) int omemo_random_func(uint8_t *data, size_t len, void *user_data) { - randombytes_buf(data, len); + gcry_randomize(data, len, GCRY_VERY_STRONG_RANDOM); return 0; } int omemo_hmac_sha256_init_func(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) { - *hmac_context = sodium_malloc(sizeof(crypto_auth_hmacsha256_state)); - return crypto_auth_hmacsha256_init(*hmac_context, key, key_len); + gcry_error_t res; + gcry_mac_hd_t hd; + + res = gcry_mac_open(&hd, GCRY_MAC_HMAC_SHA256, 0, NULL); + if (res != GPG_ERR_NO_ERROR) { + log_error("OMEMO: %s", gcry_strerror(res)); + return OMEMO_ERR_GCRYPT; + } + + *hmac_context = hd; + res = gcry_mac_setkey(hd, key, key_len); + if (res != GPG_ERR_NO_ERROR) { + log_error("OMEMO: %s", gcry_strerror(res)); + return OMEMO_ERR_GCRYPT; + } + + return 0; } int omemo_hmac_sha256_update_func(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data) { - return crypto_auth_hmacsha256_update(hmac_context, data, data_len); + gcry_error_t res; + + res = gcry_mac_write(hmac_context, data, data_len); + if (res != GPG_ERR_NO_ERROR) { + log_error("OMEMO: %s", gcry_strerror(res)); + return OMEMO_ERR_GCRYPT; + } + + return 0; } int omemo_hmac_sha256_final_func(void *hmac_context, signal_buffer **output, void *user_data) { - int ret; - unsigned char out[crypto_auth_hmacsha256_BYTES]; + gcry_error_t res; + size_t mac_len = 32; + unsigned char out[mac_len]; - if ((ret = crypto_auth_hmacsha256_final(hmac_context, out)) != 0) { - return ret; + res = gcry_mac_read(hmac_context, out, &mac_len); + if (res != GPG_ERR_NO_ERROR) { + log_error("OMEMO: %s", gcry_strerror(res)); + return OMEMO_ERR_GCRYPT; } - *output = signal_buffer_create(out, crypto_auth_hmacsha256_BYTES); + *output = signal_buffer_create(out, mac_len); return 0; } void omemo_hmac_sha256_cleanup_func(void *hmac_context, void *user_data) { - sodium_free(hmac_context); + gcry_mac_close(hmac_context); } int omemo_sha512_digest_init_func(void **digest_context, void *user_data) { - *digest_context = sodium_malloc(sizeof(crypto_hash_sha512_state)); - return crypto_hash_sha512_init(*digest_context); + gcry_error_t res; + gcry_md_hd_t hd; + + res = gcry_md_open(&hd, GCRY_MD_SHA512, 0); + if (res != GPG_ERR_NO_ERROR) { + log_error("OMEMO: %s", gcry_strerror(res)); + return OMEMO_ERR_GCRYPT; + } + + *digest_context = hd; + + return 0; } int omemo_sha512_digest_update_func(void *digest_context, const uint8_t *data, size_t data_len, void *user_data) { - return crypto_hash_sha512_update(digest_context, data, data_len); + gcry_md_write(digest_context, data, data_len); + + return 0; } int omemo_sha512_digest_final_func(void *digest_context, signal_buffer **output, void *user_data) { - int ret; - unsigned char out[crypto_hash_sha512_BYTES]; + gcry_error_t res; + unsigned char out[64]; - if ((ret = crypto_hash_sha512_final(digest_context, out)) != 0) { - return ret; + res = gcry_md_extract(digest_context, GCRY_MD_SHA512, out, 64); + if (res != GPG_ERR_NO_ERROR) { + log_error("OMEMO: %s", gcry_strerror(res)); + return OMEMO_ERR_GCRYPT; } - *output = signal_buffer_create(out, crypto_hash_sha512_BYTES); + *output = signal_buffer_create(out, 64); return 0; } void omemo_sha512_digest_cleanup_func(void *digest_context, void *user_data) { - sodium_free(digest_context); + gcry_md_close(digest_context); } int diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 905c5c22..1b1da807 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "config/account.h" #include "log.h" @@ -218,7 +218,8 @@ omemo_generate_crypto_materials(ProfAccount *account) return; } - omemo_ctx.device_id = randombytes_uniform(0x80000000); + gcry_randomize(&omemo_ctx.device_id, 4, GCRY_VERY_STRONG_RANDOM); + omemo_ctx.device_id &= 0x7fffffff; signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); @@ -245,8 +246,11 @@ omemo_generate_crypto_materials(ProfAccount *account) static void omemo_generate_short_term_crypto_materials(ProfAccount *account) { + unsigned int start; + + gcry_randomize(&start, sizeof(unsigned int), GCRY_VERY_STRONG_RANDOM); signal_protocol_key_helper_pre_key_list_node *pre_keys_head; - signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); + signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start, 100, omemo_ctx.signal); session_signed_pre_key *signed_pre_key; struct timeval tv; @@ -442,13 +446,11 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean unsigned char *ciphertext; size_t ciphertext_len; - key = sodium_malloc(AES128_GCM_KEY_LENGTH); - iv = sodium_malloc(AES128_GCM_IV_LENGTH); ciphertext_len = strlen(message) + AES128_GCM_TAG_LENGTH; ciphertext = malloc(ciphertext_len); - randombytes_buf(key, 16); - randombytes_buf(iv, 16); + key = gcry_random_bytes_secure(16, GCRY_VERY_STRONG_RANDOM); + iv = gcry_random_bytes_secure(16, GCRY_VERY_STRONG_RANDOM); res = aes128gcm_encrypt(ciphertext, &ciphertext_len, (const unsigned char * const)message, strlen(message), iv, key); if (res != 0) { @@ -516,8 +518,8 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean free(id); g_list_free_full(keys, free); free(ciphertext); - sodium_free(key); - sodium_free(iv); + gcry_free(key); + gcry_free(iv); return TRUE; } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index cffc63f1..f07d42bc 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -4,6 +4,7 @@ #include "config/account.h" #define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000 +#define OMEMO_ERR_GCRYPT -20000 typedef struct omemo_context_t omemo_context; -- cgit 1.4.1-2-gfad0 From b0c52f84ab419918d4dd0ab1fd3f9755b3687c93 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 21:23:06 +0220 Subject: Follow xep by putting gcm tag on encrypted key --- src/omemo/crypto.c | 4 ++-- src/omemo/crypto.h | 8 ++++---- src/omemo/omemo.c | 24 +++++++++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index 5119443a..d959020b 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -261,7 +261,7 @@ out: } int -aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len, const unsigned char *const plaintext, size_t plaintext_len, const unsigned char *const iv, const unsigned char *const key) +aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len, unsigned char *tag, size_t *tag_len, const unsigned char *const plaintext, size_t plaintext_len, const unsigned char *const iv, const unsigned char *const key) { gcry_error_t res; gcry_cipher_hd_t hd; @@ -284,7 +284,7 @@ aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len, const unsig goto out; } - res = gcry_cipher_gettag(hd, ciphertext + plaintext_len, AES128_GCM_TAG_LENGTH); + res = gcry_cipher_gettag(hd, tag, *tag_len); if (res != GPG_ERR_NO_ERROR) { goto out; } diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index 35c5d72a..e4a0a4ad 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -137,10 +137,10 @@ int omemo_decrypt_func(signal_buffer **output, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data); -int aes128gcm_encrypt(unsigned char *ciphertext, - size_t *ciphertext_len, const unsigned char *const cleartext, - size_t cleatext_len, const unsigned char *const iv, - const unsigned char *const key); +int aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len, + unsigned char *tag, size_t *tag_len, + const unsigned char *const plaintext, size_t plaintext_len, + const unsigned char *const iv, const unsigned char *const key); int aes128gcm_decrypt(unsigned char *plaintext, size_t *plaintext_len, const unsigned char *const ciphertext, diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 1b1da807..66793085 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -444,19 +444,27 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean unsigned char *key; unsigned char *iv; unsigned char *ciphertext; - size_t ciphertext_len; + unsigned char *tag; + unsigned char *key_tag; + size_t ciphertext_len, tag_len; - ciphertext_len = strlen(message) + AES128_GCM_TAG_LENGTH; + ciphertext_len = strlen(message); ciphertext = malloc(ciphertext_len); + tag_len = AES128_GCM_TAG_LENGTH; + tag = gcry_malloc_secure(tag_len); + key_tag = gcry_malloc_secure(AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH); - key = gcry_random_bytes_secure(16, GCRY_VERY_STRONG_RANDOM); - iv = gcry_random_bytes_secure(16, GCRY_VERY_STRONG_RANDOM); + key = gcry_random_bytes_secure(AES128_GCM_KEY_LENGTH, GCRY_VERY_STRONG_RANDOM); + iv = gcry_random_bytes_secure(AES128_GCM_IV_LENGTH, GCRY_VERY_STRONG_RANDOM); - res = aes128gcm_encrypt(ciphertext, &ciphertext_len, (const unsigned char * const)message, strlen(message), iv, key); + res = aes128gcm_encrypt(ciphertext, &ciphertext_len, tag, &tag_len, (const unsigned char * const)message, strlen(message), iv, key); if (res != 0) { return FALSE; } + memcpy(key_tag, key, AES128_GCM_KEY_LENGTH); + memcpy(key_tag + AES128_GCM_KEY_LENGTH, tag, AES128_GCM_TAG_LENGTH); + GList *keys = NULL; GList *device_ids_iter; for (device_ids_iter = recipient_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { @@ -472,7 +480,7 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean continue; } - res = session_cipher_encrypt(cipher, key, AES128_GCM_KEY_LENGTH, &ciphertext); + res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); if (res != 0) { continue; } @@ -498,7 +506,7 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean continue; } - res = session_cipher_encrypt(cipher, key, AES128_GCM_KEY_LENGTH, &ciphertext); + res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); if (res != 0) { continue; } @@ -520,6 +528,8 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean free(ciphertext); gcry_free(key); gcry_free(iv); + gcry_free(tag); + gcry_free(key_tag); return TRUE; } -- cgit 1.4.1-2-gfad0 From da0376a6a9b83756efffed6fb24fafd72f0fc36e Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 21:27:25 +0220 Subject: Check received gcm tag --- src/omemo/crypto.c | 11 ++++++----- src/omemo/crypto.h | 2 +- src/omemo/omemo.c | 12 +++++++++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index d959020b..9d64a701 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -295,7 +295,7 @@ out: } int -aes128gcm_decrypt(unsigned char *plaintext, size_t *plaintext_len, const unsigned char *const ciphertext, size_t ciphertext_len, const unsigned char *const iv, const unsigned char *const key) +aes128gcm_decrypt(unsigned char *plaintext, size_t *plaintext_len, const unsigned char *const ciphertext, size_t ciphertext_len, const unsigned char *const iv, const unsigned char *const key, const unsigned char *const tag) { gcry_error_t res; gcry_cipher_hd_t hd; @@ -319,10 +319,11 @@ aes128gcm_decrypt(unsigned char *plaintext, size_t *plaintext_len, const unsigne if (res != GPG_ERR_NO_ERROR) { goto out; } - //res = gcry_cipher_checktag(hd, ciphertext + ciphertext_len - AES128_GCM_TAG_LENGTH, AES128_GCM_TAG_LENGTH); - //if (res != GPG_ERR_NO_ERROR) { - // goto out; - //} + + res = gcry_cipher_checktag(hd, tag, AES128_GCM_TAG_LENGTH); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } out: gcry_cipher_close(hd); diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index e4a0a4ad..4b882455 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -145,4 +145,4 @@ int aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len, int aes128gcm_decrypt(unsigned char *plaintext, size_t *plaintext_len, const unsigned char *const ciphertext, size_t ciphertext_len, const unsigned char *const iv, - const unsigned char *const key); + const unsigned char *const key, const unsigned char *const tag); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 66793085..98448a73 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -577,10 +577,20 @@ omemo_on_message_recv(const char *const from, uint32_t sid, return NULL; } + if (signal_buffer_len(plaintext_key) != AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH) { + log_error("OMEMO: invalid key length"); + signal_buffer_free(plaintext_key); + return NULL; + } + size_t plaintext_len = payload_len; unsigned char *plaintext = malloc(plaintext_len + 1); - res = aes128gcm_decrypt(plaintext, &plaintext_len, payload, payload_len, iv, signal_buffer_data(plaintext_key)); + res = aes128gcm_decrypt(plaintext, &plaintext_len, payload, payload_len, iv, + signal_buffer_data(plaintext_key), + signal_buffer_data(plaintext_key) + AES128_GCM_KEY_LENGTH); if (res != 0) { + log_error("OMEMO: cannot decrypt message: %s", gcry_strerror(res)); + signal_buffer_free(plaintext_key); free(plaintext); return NULL; } -- cgit 1.4.1-2-gfad0 From 2718183d27b20eae88d808b2cf181b22610c5e26 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 19:22:46 +0140 Subject: Enable start of OMEMO in existing chat win --- src/command/cmd_funcs.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 1ce034d9..5ce9b7f4 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7950,6 +7950,36 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) omemo_start_session(barejid); chatwin->is_omemo = TRUE; + } else { + if (window->type != WIN_CHAT) { + win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session."); + return TRUE; + } + + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + if (chatwin->pgp_send) { + win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session."); + return TRUE; + } + + if (chatwin->is_otr) { + win_println(window, THEME_DEFAULT, '!', "You must disable OTR encryption before starting an OMEMO session."); + return TRUE; + } + + if (chatwin->is_omemo) { + win_println(window, THEME_DEFAULT, '!', "You are already in an OMEMO session."); + return TRUE; + } + + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); + return TRUE; + } + + omemo_start_session(chatwin->barejid); + chatwin->is_omemo = TRUE; } return TRUE; -- cgit 1.4.1-2-gfad0 From ba0fa6e5360ce9350df469724d912be5644ac62b Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 19:35:00 +0140 Subject: Add OMEMO commands autocompletion --- src/command/cmd_ac.c | 31 +++++++++++++++++++++++++++++++ src/command/cmd_defs.c | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 58ad758a..daf4f3a9 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -69,6 +69,7 @@ static char* _group_autocomplete(ProfWin *window, const char *const input, gbool static char* _bookmark_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _otr_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _pgp_autocomplete(ProfWin *window, const char *const input, gboolean previous); +static char* _omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _connect_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _alias_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _join_autocomplete(ProfWin *window, const char *const input, gboolean previous); @@ -157,6 +158,7 @@ static Autocomplete bookmark_property_ac; static Autocomplete otr_ac; static Autocomplete otr_log_ac; static Autocomplete otr_policy_ac; +static Autocomplete omemo_ac; static Autocomplete connect_property_ac; static Autocomplete tls_property_ac; static Autocomplete alias_ac; @@ -574,6 +576,10 @@ cmd_ac_init(void) autocomplete_add(otr_policy_ac, "opportunistic"); autocomplete_add(otr_policy_ac, "always"); + omemo_ac = autocomplete_new(); + autocomplete_add(omemo_ac, "gen"); + autocomplete_add(omemo_ac, "start"); + connect_property_ac = autocomplete_new(); autocomplete_add(connect_property_ac, "server"); autocomplete_add(connect_property_ac, "port"); @@ -1052,6 +1058,7 @@ cmd_ac_reset(ProfWin *window) autocomplete_reset(otr_ac); autocomplete_reset(otr_log_ac); autocomplete_reset(otr_policy_ac); + autocomplete_reset(omemo_ac); autocomplete_reset(connect_property_ac); autocomplete_reset(tls_property_ac); autocomplete_reset(alias_ac); @@ -1179,6 +1186,7 @@ cmd_ac_uninit(void) autocomplete_free(otr_ac); autocomplete_free(otr_log_ac); autocomplete_free(otr_policy_ac); + autocomplete_free(omemo_ac); autocomplete_free(connect_property_ac); autocomplete_free(tls_property_ac); autocomplete_free(alias_ac); @@ -1438,6 +1446,7 @@ _cmd_ac_complete_params(ProfWin *window, const char *const input, gboolean previ g_hash_table_insert(ac_funcs, "/autoconnect", _autoconnect_autocomplete); g_hash_table_insert(ac_funcs, "/otr", _otr_autocomplete); g_hash_table_insert(ac_funcs, "/pgp", _pgp_autocomplete); + g_hash_table_insert(ac_funcs, "/omemo", _omemo_autocomplete); g_hash_table_insert(ac_funcs, "/connect", _connect_autocomplete); g_hash_table_insert(ac_funcs, "/alias", _alias_autocomplete); g_hash_table_insert(ac_funcs, "/join", _join_autocomplete); @@ -2117,6 +2126,28 @@ _pgp_autocomplete(ProfWin *window, const char *const input, gboolean previous) return NULL; } +static char* +_omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous) +{ + char *found = NULL; + + jabber_conn_status_t conn_status = connection_get_status(); + + if (conn_status == JABBER_CONNECTED) { + found = autocomplete_param_with_func(input, "/omemo start", roster_contact_autocomplete, previous); + if (found) { + return found; + } + } + + found = autocomplete_param_with_ac(input, "/omemo", omemo_ac, TRUE, previous); + if (found) { + return found; + } + + return NULL; +} + static char* _plugins_autocomplete(ProfWin *window, const char *const input, gboolean previous) { diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 265af119..3cd8330e 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2331,7 +2331,7 @@ static struct cmd_t command_defs[] = }, { "/omemo", - parse_args, 1, 3, NULL, + parse_args, 1, 2, NULL, CMD_SUBFUNCS( { "gen", cmd_omemo_gen }, { "start", cmd_omemo_start }) -- cgit 1.4.1-2-gfad0 From bf8b89c568cbc6e2bc2c7dfe058b13a6ec33215a Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 17:10:28 +0100 Subject: Randomly choose a prekey on session build --- src/omemo/omemo.c | 34 ++++++++++++++++++++++++++-------- src/omemo/omemo.h | 3 ++- src/xmpp/omemo.c | 41 ++++++++++++++++++++++------------------- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 98448a73..b7c31cd6 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -30,6 +30,7 @@ static void unlock(void *user_data); static void omemo_log(int level, const char *message, size_t len, void *user_data); static gboolean handle_own_device_list(const char *const jid, GList *device_list); static gboolean handle_device_list_start_session(const char *const jid, GList *device_list); +static void free_omemo_key(omemo_key_t *key); typedef gboolean (*OmemoDeviceListHandler)(const char *const jid, GList *device_list); @@ -396,11 +397,10 @@ omemo_set_device_list(const char *const jid, GList * device_list) void omemo_start_device_session(const char *const jid, uint32_t device_id, - uint32_t prekey_id, const unsigned char *const prekey_raw, size_t prekey_len, - uint32_t signed_prekey_id, const unsigned char *const signed_prekey_raw, - size_t signed_prekey_len, const unsigned char *const signature, - size_t signature_len, const unsigned char *const identity_key_raw, - size_t identity_key_len) + GList *prekeys, uint32_t signed_prekey_id, + const unsigned char *const signed_prekey_raw, size_t signed_prekey_len, + const unsigned char *const signature, size_t signature_len, + const unsigned char *const identity_key_raw, size_t identity_key_len) { log_info("Start OMEMO session with %s device %d", jid, device_id); session_pre_key_bundle *bundle; @@ -414,15 +414,22 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, session_builder *builder; session_builder_create(&builder, omemo_ctx.store, address, omemo_ctx.signal); - ec_public_key *prekey; - curve_decode_point(&prekey, prekey_raw, prekey_len, omemo_ctx.signal); + int prekey_index; + gcry_randomize(&prekey_index, sizeof(int), GCRY_STRONG_RANDOM); + prekey_index %= g_list_length(prekeys); + omemo_key_t *prekey = g_list_nth_data(prekeys, prekey_index); + + ec_public_key *prekey_public; + curve_decode_point(&prekey_public, prekey->data, prekey->length, omemo_ctx.signal); ec_public_key *signed_prekey; curve_decode_point(&signed_prekey, signed_prekey_raw, signed_prekey_len, omemo_ctx.signal); ec_public_key *identity_key; curve_decode_point(&identity_key, identity_key_raw, identity_key_len, omemo_ctx.signal); - session_pre_key_bundle_create(&bundle, 0, device_id, prekey_id, prekey, signed_prekey_id, signed_prekey, signature, signature_len, identity_key); + session_pre_key_bundle_create(&bundle, 0, device_id, prekey->id, prekey_public, signed_prekey_id, signed_prekey, signature, signature_len, identity_key); session_builder_process_pre_key_bundle(builder, bundle); + + g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); } gboolean @@ -662,3 +669,14 @@ handle_device_list_start_session(const char *const jid, GList *device_list) return FALSE; } + +static void +free_omemo_key(omemo_key_t *key) +{ + if (key == NULL) { + return; + } + + free((void *)key->data); + free(key); +} diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index f07d42bc..84f21e64 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -13,6 +13,7 @@ typedef struct omemo_key { size_t length; gboolean prekey; uint32_t device_id; + uint32_t id; } omemo_key_t; void omemo_init(void); @@ -27,7 +28,7 @@ void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); void omemo_start_session(const char *const barejid); -void omemo_start_device_session(const char *const jid, uint32_t device_id, uint32_t prekey_id, const unsigned char *const prekey, size_t prekey_len, 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); +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); gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt); diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 25057d55..f68f7d6e 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -129,22 +129,26 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons return 1; } - /* Should be random */ - xmpp_stanza_t *prekey = xmpp_stanza_get_children(prekeys); - if (!prekey) { - return 1; - } - const char *prekey_id_text = xmpp_stanza_get_attribute(prekey, "preKeyId"); - if (!prekey_id_text) { - return 1; - } - uint32_t prekey_id = strtoul(prekey_id_text, NULL, 10); - xmpp_stanza_t *prekey_text = xmpp_stanza_get_children(prekey); - if (!prekey_text) { - return 1; + GList *prekeys_list = NULL; + xmpp_stanza_t *prekey; + for (prekey = xmpp_stanza_get_children(prekeys); prekey != NULL; prekey = xmpp_stanza_get_next(prekey)) { + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + + const char *prekey_id_text = xmpp_stanza_get_attribute(prekey, "preKeyId"); + if (!prekey_id_text) { + return 1; + } + key->id = strtoul(prekey_id_text, NULL, 10); + xmpp_stanza_t *prekey_text = xmpp_stanza_get_children(prekey); + if (!prekey_text) { + return 1; + } + key->data = g_base64_decode(xmpp_stanza_get_text(prekey_text), &key->length); + key->prekey = TRUE; + key->device_id = device_id; + + prekeys_list = g_list_append(prekeys_list, key); } - size_t prekey_len; - unsigned char *prekey_raw = g_base64_decode(xmpp_stanza_get_text(prekey_text), &prekey_len); xmpp_stanza_t *signed_prekey = xmpp_stanza_get_child_by_name(bundle, "signedPreKeyPublic"); if (!signed_prekey) { @@ -184,10 +188,9 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons size_t identity_key_len; unsigned char *identity_key_raw = g_base64_decode(xmpp_stanza_get_text(identity_key_text), &identity_key_len); - omemo_start_device_session(from, device_id, prekey_id, prekey_raw, - prekey_len, signed_prekey_id, signed_prekey_raw, signed_prekey_len, - signed_prekey_signature_raw, signed_prekey_signature_len, - identity_key_raw, identity_key_len); + omemo_start_device_session(from, device_id, prekeys_list, signed_prekey_id, + signed_prekey_raw, signed_prekey_len, signed_prekey_signature_raw, + signed_prekey_signature_len, identity_key_raw, identity_key_len); return 1; } -- cgit 1.4.1-2-gfad0 From 1b5848fb235e7b6fe6bfcd64ce9bc57477df8b64 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 20:17:33 +0140 Subject: Start new session upon prekey reception --- src/omemo/omemo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index b7c31cd6..3fadefd4 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -573,6 +573,7 @@ omemo_on_message_recv(const char *const from, uint32_t sid, if (key->prekey) { pre_key_signal_message *message; + omemo_bundle_request(from, sid, omemo_start_device_session_handle_bundle, free, strdup(from)); pre_key_signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); res = session_cipher_decrypt_pre_key_signal_message(cipher, message, NULL, &plaintext_key); } else { -- cgit 1.4.1-2-gfad0 From 5eb66aea267c53c63af6dd76fe2555f113051fcd Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 6 Mar 2019 20:24:28 +0140 Subject: Replace used prekey in bundle --- src/omemo/omemo.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 3fadefd4..169623f4 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -573,8 +573,23 @@ omemo_on_message_recv(const char *const from, uint32_t sid, if (key->prekey) { pre_key_signal_message *message; - omemo_bundle_request(from, sid, omemo_start_device_session_handle_bundle, free, strdup(from)); + pre_key_signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); + + /* Replace used pre_key in bundle */ + uint32_t pre_key_id = pre_key_signal_message_get_pre_key_id(message); + g_hash_table_remove(omemo_ctx.pre_key_store, GINT_TO_POINTER(pre_key_id)); + ec_key_pair *ec_pair; + session_pre_key *new_pre_key; + curve_generate_key_pair(omemo_ctx.signal, &ec_pair); + session_pre_key_create(&new_pre_key, pre_key_id, ec_pair); + g_hash_table_insert(omemo_ctx.pre_key_store, GINT_TO_POINTER(pre_key_id), new_pre_key); + omemo_bundle_publish(); + + /* Start a new session */ + omemo_bundle_request(from, sid, omemo_start_device_session_handle_bundle, free, strdup(from)); + + /* Try to decrypt message anyway, it will fail */ res = session_cipher_decrypt_pre_key_signal_message(cipher, message, NULL, &plaintext_key); } else { signal_message *message; -- cgit 1.4.1-2-gfad0 From 55407ee15f0ebdf93c076ae5ea28ec865497b121 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 7 Mar 2019 08:00:29 +0140 Subject: Add OMEMO session long term storage --- src/omemo/omemo.c | 115 ++++++++++++++++++++++++++++++++++++++++-------------- src/omemo/omemo.h | 2 + src/omemo/store.c | 8 ++++ 3 files changed, 95 insertions(+), 30 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 169623f4..3a771b6b 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -52,6 +52,8 @@ struct omemo_context_t { GHashTable *device_ids; GString *identity_filename; GKeyFile *identity_keyfile; + GString *sessions_filename; + GKeyFile *sessions_keyfile; }; static omemo_context omemo_ctx; @@ -169,15 +171,18 @@ omemo_on_connect(ProfAccount *account) omemo_ctx.identity_filename = g_string_new(basedir->str); g_string_append(omemo_ctx.identity_filename, "identity.txt"); + omemo_ctx.sessions_filename = g_string_new(basedir->str); + g_string_append(omemo_ctx.sessions_filename, "sessions.txt"); + errno = 0; int res = g_mkdir_with_parents(basedir->str, S_IRWXU); if (res == -1) { char *errmsg = strerror(errno); if (errmsg) { - log_error("Error creating directory: %s, %s", omemo_ctx.identity_filename->str, errmsg); + log_error("Error creating directory: %s, %s", basedir->str, errmsg); } else { - log_error("Error creating directory: %s", omemo_ctx.identity_filename->str); + log_error("Error creating directory: %s", basedir->str); } } @@ -208,6 +213,32 @@ omemo_on_connect(ProfAccount *account) omemo_generate_short_term_crypto_materials(account); } + + omemo_ctx.sessions_keyfile = g_key_file_new(); + if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + int i; + char **groups = g_key_file_get_groups(omemo_ctx.sessions_keyfile, NULL); + for (i = 0; groups[i] != NULL; i++) { + int j; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(omemo_ctx.session_store, groups[i]); + if (!device_store) { + device_store = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); + g_hash_table_insert(omemo_ctx.session_store, groups[i], device_store); + } + + char **keys = g_key_file_get_keys(omemo_ctx.sessions_keyfile, groups[i], NULL, NULL); + for (j = 0; keys[j] != NULL; j++) { + uint32_t id = strtoul(keys[j], NULL, 10); + char *record_b64 = g_key_file_get_string(omemo_ctx.sessions_keyfile, groups[i], keys[j], NULL); + size_t record_len; + unsigned char *record = g_base64_decode(record_b64, &record_len); + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(device_store, GINT_TO_POINTER(id), buffer); + } + } + } } void @@ -395,6 +426,22 @@ omemo_set_device_list(const char *const jid, GList * device_list) free(barejid); } +GKeyFile * +omemo_sessions_keyfile(void) +{ + return omemo_ctx.sessions_keyfile; +} + +void +omemo_sessions_keyfile_save(void) +{ + GError *error = NULL; + + if (!g_key_file_save_to_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, &error)) { + log_error("Error saving OMEMO sessions to: %s, %s", omemo_ctx.sessions_filename->str, error->message); + } +} + void omemo_start_device_session(const char *const jid, uint32_t device_id, GList *prekeys, uint32_t signed_prekey_id, @@ -402,34 +449,42 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const signature, size_t signature_len, const unsigned char *const identity_key_raw, size_t identity_key_len) { - log_info("Start OMEMO session with %s device %d", jid, device_id); - session_pre_key_bundle *bundle; - signal_protocol_address *address; - - address = malloc(sizeof(signal_protocol_address)); - address->name = strdup(jid); - address->name_len = strlen(jid); - address->device_id = device_id; - - session_builder *builder; - session_builder_create(&builder, omemo_ctx.store, address, omemo_ctx.signal); - - int prekey_index; - gcry_randomize(&prekey_index, sizeof(int), GCRY_STRONG_RANDOM); - prekey_index %= g_list_length(prekeys); - omemo_key_t *prekey = g_list_nth_data(prekeys, prekey_index); - - ec_public_key *prekey_public; - curve_decode_point(&prekey_public, prekey->data, prekey->length, omemo_ctx.signal); - ec_public_key *signed_prekey; - curve_decode_point(&signed_prekey, signed_prekey_raw, signed_prekey_len, omemo_ctx.signal); - ec_public_key *identity_key; - curve_decode_point(&identity_key, identity_key_raw, identity_key_len, omemo_ctx.signal); - - session_pre_key_bundle_create(&bundle, 0, device_id, prekey->id, prekey_public, signed_prekey_id, signed_prekey, signature, signature_len, identity_key); - session_builder_process_pre_key_bundle(builder, bundle); - - g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); + signal_protocol_address address = { + .name = strdup(jid), + .name_len = strlen(jid), + .device_id = device_id, + }; + + if (!contains_session(&address, omemo_ctx.session_store)) { + log_info("Create OMEMO session with %s device %d", jid, device_id); + session_pre_key_bundle *bundle; + signal_protocol_address *address; + + address = malloc(sizeof(signal_protocol_address)); + address->name = strdup(jid); + address->name_len = strlen(jid); + address->device_id = device_id; + + session_builder *builder; + session_builder_create(&builder, omemo_ctx.store, address, omemo_ctx.signal); + + int prekey_index; + gcry_randomize(&prekey_index, sizeof(int), GCRY_STRONG_RANDOM); + prekey_index %= g_list_length(prekeys); + omemo_key_t *prekey = g_list_nth_data(prekeys, prekey_index); + + ec_public_key *prekey_public; + curve_decode_point(&prekey_public, prekey->data, prekey->length, omemo_ctx.signal); + ec_public_key *signed_prekey; + curve_decode_point(&signed_prekey, signed_prekey_raw, signed_prekey_len, omemo_ctx.signal); + ec_public_key *identity_key; + curve_decode_point(&identity_key, identity_key_raw, identity_key_len, omemo_ctx.signal); + + session_pre_key_bundle_create(&bundle, 0, device_id, prekey->id, prekey_public, signed_prekey_id, signed_prekey, signature, signature_len, identity_key); + session_builder_process_pre_key_bundle(builder, bundle); + + g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); + } } gboolean diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 84f21e64..ef43617d 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -26,6 +26,8 @@ void omemo_signed_prekey(unsigned char **output, size_t *length); void omemo_signed_prekey_signature(unsigned char **output, size_t *length); void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); +GKeyFile *omemo_sessions_keyfile(void); +void omemo_sessions_keyfile_save(void); void omemo_start_session(const char *const barejid); 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); diff --git a/src/omemo/store.c b/src/omemo/store.c index 9fec33e5..93289b90 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -1,6 +1,7 @@ #include #include +#include "omemo/omemo.h" #include "omemo/store.h" GHashTable * @@ -90,6 +91,13 @@ store_session(const signal_protocol_address *address, uint8_t *record, signal_buffer *buffer = signal_buffer_create(record, record_len); g_hash_table_insert(device_store, GINT_TO_POINTER(address->device_id), buffer); + + + char *record_b64 = g_base64_encode(record, record_len); + g_key_file_set_string(omemo_sessions_keyfile(), address->name, g_strdup_printf("%d", address->device_id), record_b64); + + omemo_sessions_keyfile_save(); + return SG_SUCCESS; } -- cgit 1.4.1-2-gfad0 From b3be504e841be55f917a3841d9cddf4ce076cc04 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 7 Mar 2019 19:04:33 +0140 Subject: Add OMEMO fingerprint command --- src/command/cmd_ac.c | 1 + src/command/cmd_defs.c | 9 ++++++--- src/command/cmd_funcs.c | 28 +++++++++++++++++++++++++++ src/command/cmd_funcs.h | 1 + src/omemo/omemo.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ src/omemo/omemo.h | 3 +++ 6 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index daf4f3a9..55f05c7c 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -579,6 +579,7 @@ cmd_ac_init(void) omemo_ac = autocomplete_new(); autocomplete_add(omemo_ac, "gen"); autocomplete_add(omemo_ac, "start"); + autocomplete_add(omemo_ac, "fingerprint"); connect_property_ac = autocomplete_new(); autocomplete_add(connect_property_ac, "server"); diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 3cd8330e..7b2df39b 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2334,19 +2334,22 @@ static struct cmd_t command_defs[] = parse_args, 1, 2, NULL, CMD_SUBFUNCS( { "gen", cmd_omemo_gen }, - { "start", cmd_omemo_start }) + { "start", cmd_omemo_start }, + { "fingerprint", cmd_omemo_fingerprint }) CMD_NOMAINFUNC CMD_TAGS( CMD_TAG_CHAT, CMD_TAG_UI) CMD_SYN( "/omemo gen", - "/omemo start []") + "/omemo start []", + "/omemo fingerprint") CMD_DESC( "Omemo commands to manage keys, and perform encryption during chat sessions.") CMD_ARGS( { "gen", "Generate OMEMO crytographic materials for current account." }, - { "start []", "Start an OMEMO session with contact, or current recipient if omitted." }) + { "start []", "Start an OMEMO session with contact, or current recipient if omitted." }, + { "fingerprint", "Show current device fingerprint." }) CMD_EXAMPLES( "/omemo gen", "/omemo start buddy@buddychat.org") diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 5ce9b7f4..70940f17 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7988,3 +7988,31 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) return TRUE; #endif } + +gboolean +cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OMEMO information."); + return TRUE; + } + + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); + return TRUE; + } + + char *fingerprint = omemo_own_fingerprint(); + char *formated_fingerprint = omemo_format_fingerprint(fingerprint); + cons_show("%s", formated_fingerprint); + + free(fingerprint); + free(formated_fingerprint); + + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index f39e8c17..4809f703 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -216,5 +216,6 @@ gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 3a771b6b..b293022f 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -679,6 +679,57 @@ omemo_on_message_recv(const char *const from, uint32_t sid, } +char * +omemo_format_fingerprint(const char *const fingerprint) +{ + char *output = malloc(strlen(fingerprint) + strlen(fingerprint) / 8 + 1); + + int i, j; + for (i = 0, j = 0; i < strlen(fingerprint); i++) { + if (i > 0 && i % 8 == 0) { + output[j++] = '-'; + } + output[j++] = fingerprint[i]; + } + + output[strlen(fingerprint) + strlen(fingerprint) / 8] = '\0'; + + return output; +} + +char * +omemo_own_fingerprint() +{ + signal_buffer *public = omemo_ctx.identity_key_store.public; + /* Skip first byte corresponding to signal base type */ + return omemo_fingerprint(signal_buffer_data(public) + 1, signal_buffer_len(public) - 1); +} + +char * +omemo_fingerprint(const unsigned char *const identity_key_public, size_t len) +{ + int i; + char *fingerprint = malloc(len * 2 + 1); + + for (i = 0; i < len; i++) { + fingerprint[i * 2] = (identity_key_public[i] & 0xf0) >> 4; + fingerprint[i * 2] += 0x30; + if (fingerprint[i * 2] > 0x39) { + fingerprint[i * 2] += 0x27; + } + + fingerprint[(i * 2) + 1] = identity_key_public[i] & 0x0f; + fingerprint[(i * 2) + 1] += 0x30; + if (fingerprint[(i * 2) + 1] > 0x39) { + fingerprint[(i * 2) + 1] += 0x27; + } + } + + fingerprint[len * 2] = '\0'; + + return fingerprint; +} + static void lock(void *user_data) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index ef43617d..88c6d27c 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -28,6 +28,9 @@ void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); GKeyFile *omemo_sessions_keyfile(void); void omemo_sessions_keyfile_save(void); +char *omemo_fingerprint(const unsigned char *const identity_key_public, size_t len); +char *omemo_format_fingerprint(const char *const fingerprint); +char *omemo_own_fingerprint(); void omemo_start_session(const char *const barejid); 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); -- cgit 1.4.1-2-gfad0 From 97c661271b825120b841860771155aa5592019e7 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 11 Mar 2019 06:46:02 +0140 Subject: Add support for libsignal-protocol-c 2.3.2 --- configure.ac | 32 ++++++++++++++++++-------------- src/omemo/store.c | 15 +++++++++++++++ src/omemo/store.h | 10 ++++++++++ src/xmpp/message.c | 2 +- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index edf72d60..0c4d005c 100644 --- a/configure.ac +++ b/configure.ac @@ -266,23 +266,27 @@ if test "x$enable_otr" != xno; then [AC_MSG_NOTICE([libotr not found, otr encryption support not enabled])])]) fi -AM_CONDITIONAL([BUILD_OMEMO], [false]) +AM_CONDITIONAL([BUILD_OMEMO], [true]) if test "x$enable_omemo" != xno; then - AC_CHECK_LIB([signal-protocol-c], [signal_context_create], - [AM_CONDITIONAL([BUILD_OMEMO], [true]) - AC_DEFINE([HAVE_OMEMO], [1], [Have omemo]), - LIBS="-lsignal-protocol-c $LIBS"], - [AS_IF([test "x$enable_omemo" = xyes], - [AC_MSG_ERROR([libsignal-protocol-c is required for omemo support])], - [AC_MSG_NOTICE([libsignal-protocol-c not found, omemo support not enabled])])]) + PKG_CHECK_MODULES([libsignal], [libsignal-protocol-c >= 2.3.2], + [LIBS="-lsignal-protocol-c $LIBS"], + [AC_MSG_NOTICE([libsignal >= 2.3.2 not found, checking for libsignal 2.3.x...]) + PKG_CHECK_MODULES([libsignal], [libsignal-protocol-c >= 2.3.0], + [LIBS="-lsignal-protocol-c $LIBS" + AC_DEFINE([HAVE_LIBSIGNAL_LT_2_3_2], [1], [Have libsignal-protocol-c < 2.3.2])], + [AM_CONDITIONAL([BUILD_OMEMO], [false]) + AS_IF([test "x$enable_omemo" = xyes], + [AC_MSG_ERROR([libsignal-protocol-c is required for omemo support])], + [AC_MSG_NOTICE([libsignal-protocol-c not found, omemo support not enabled])])])]) AC_CHECK_LIB([gcrypt], [gcry_check_version], - [AM_CONDITIONAL([BUILD_OMEMO], [true]) - AC_DEFINE([HAVE_OMEMO], [1], [Have omemo]), - LIBS="-lgcrypt $LIBS"], - [AS_IF([test "x$enable_omemo" = xyes], - [AC_MSG_ERROR([gcrypt is required for omemo support])], - [AC_MSG_NOTICE([gcrypt not found, omemo support not enabled])])]) + [LIBS="-lgcrypt $LIBS"], + [AM_CONDITIONAL([BUILD_OMEMO], [false]) + AS_IF([test "x$enable_omemo" = xyes], + [AC_MSG_ERROR([gcrypt is required for omemo support])], + [AC_MSG_NOTICE([gcrypt not found, omemo support not enabled])])]) + + AM_COND_IF([BUILD_OMEMO], [AC_DEFINE([HAVE_OMEMO], [1], [Have OMEMO])]) fi AS_IF([test "x$with_themes" = xno], diff --git a/src/omemo/store.c b/src/omemo/store.c index 93289b90..eee58e19 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -1,6 +1,7 @@ #include #include +#include "config.h" #include "omemo/omemo.h" #include "omemo/store.h" @@ -30,9 +31,15 @@ identity_key_store_new(identity_key_store_t *identity_key_store) identity_key_store->public = NULL; } +#ifdef HAVE_LIBSIGNAL_LT_2_3_2 int load_session(signal_buffer **record, const signal_protocol_address *address, void *user_data) +#else +int +load_session(signal_buffer **record, signal_buffer **user_record, + const signal_protocol_address *address, void *user_data) +#endif { GHashTable *session_store = (GHashTable *)user_data; GHashTable *device_store = NULL; @@ -76,9 +83,17 @@ get_sub_device_sessions(signal_int_list **sessions, const char *name, return SG_SUCCESS; } +#ifdef HAVE_LIBSIGNAL_LT_2_3_2 int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, void *user_data) +#else +int +store_session(const signal_protocol_address *address, + uint8_t *record, size_t record_len, + uint8_t *user_record, size_t user_record_len, + void *user_data) +#endif { GHashTable *session_store = (GHashTable *)user_data; GHashTable *device_store = NULL; diff --git a/src/omemo/store.h b/src/omemo/store.h index ad33d5a5..986b7c42 100644 --- a/src/omemo/store.h +++ b/src/omemo/store.h @@ -1,5 +1,7 @@ #include +#include "config.h" + #define OMEMO_STORE_GROUP_IDENTITY "identity" #define OMEMO_STORE_KEY_DEVICE_ID "device_id" #define OMEMO_STORE_KEY_REGISTRATION_ID "registration_id" @@ -28,7 +30,11 @@ void identity_key_store_new(identity_key_store_t *identity_key_store); * @param address the address of the remote client * @return 1 if the session was loaded, 0 if the session was not found, negative on failure */ +#ifdef HAVE_LIBSIGNAL_LT_2_3_2 int load_session(signal_buffer **record, const signal_protocol_address *address, void *user_data); +#else +int load_session(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address, void *user_data); +#endif /** * Returns all known devices with active sessions for a recipient @@ -50,7 +56,11 @@ int get_sub_device_sessions(signal_int_list **sessions, const char *name, size_t * @param record_len length of the serialized session record * @return 0 on success, negative on failure */ +#ifdef HAVE_LIBSIGNAL_LT_2_3_2 int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, void *user_data); +#else +int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data); +#endif /** * Determine whether there is a committed session record for a diff --git a/src/xmpp/message.c b/src/xmpp/message.c index d078e1e7..6ca63b08 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -836,7 +836,7 @@ _private_chat_handler(xmpp_stanza_t *const stanza, const char *const fulljid) static gboolean _handle_carbons(xmpp_stanza_t *const stanza) { - char *message_txt; + char *message_txt = NULL; xmpp_stanza_t *carbons = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CARBONS); if (!carbons) { return FALSE; -- cgit 1.4.1-2-gfad0 From 249701fe7a20247b85f4bad59158ba1215419cc3 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 11 Mar 2019 07:51:21 +0140 Subject: Add OMEMO status in build information --- src/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.c b/src/main.c index d2392a2b..6060ca27 100644 --- a/src/main.c +++ b/src/main.c @@ -138,6 +138,12 @@ main(int argc, char **argv) g_print("PGP support: Disabled\n"); #endif +#ifdef HAVE_OMEMO + g_print("OMEMO support: Enabled\n"); +#else + g_print("OMEMO support: Disabled\n"); +#endif + #ifdef HAVE_C g_print("C plugins: Enabled\n"); #else -- cgit 1.4.1-2-gfad0 From 23485eb4e7e2f31cb72ba03c3ec2110bafe25a36 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 7 Mar 2019 19:38:15 +0140 Subject: Don't trust unknown identity keys Also add long terme storage of known identity keys. If a key is stored it means it is trusted. --- src/omemo/omemo.c | 141 +++++++++++++++++++++++++++++++++++------------------- src/omemo/omemo.h | 2 + src/omemo/store.c | 14 ++++-- src/omemo/store.h | 3 +- 4 files changed, 105 insertions(+), 55 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index b293022f..d649ff7e 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -25,6 +25,8 @@ static gboolean loaded; static void omemo_generate_short_term_crypto_materials(ProfAccount *account); +static void load_identity(void); +static void load_sessions(void); static void lock(void *user_data); static void unlock(void *user_data); static void omemo_log(int level, const char *message, size_t len, void *user_data); @@ -188,64 +190,19 @@ omemo_on_connect(ProfAccount *account) omemo_ctx.identity_keyfile = g_key_file_new(); if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { - omemo_ctx.device_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, NULL); - omemo_ctx.registration_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, NULL); - - char *identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, NULL); - size_t identity_key_public_len; - unsigned char *identity_key_public = g_base64_decode(identity_key_public_b64, &identity_key_public_len); - omemo_ctx.identity_key_store.public = signal_buffer_create(identity_key_public, identity_key_public_len); - - char *identity_key_private_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, NULL); - size_t identity_key_private_len; - unsigned char *identity_key_private = g_base64_decode(identity_key_private_b64, &identity_key_private_len); - omemo_ctx.identity_key_store.private = signal_buffer_create(identity_key_private, identity_key_private_len); - signal_buffer_create(identity_key_private, identity_key_private_len); - - ec_public_key *public_key; - curve_decode_point(&public_key, identity_key_public, identity_key_public_len, omemo_ctx.signal); - ec_private_key *private_key; - curve_decode_private_point(&private_key, identity_key_private, identity_key_private_len, omemo_ctx.signal); - ratchet_identity_key_pair_create(&omemo_ctx.identity_key_pair, public_key, private_key); - - g_free(identity_key_public); - g_free(identity_key_private); - + load_identity(); omemo_generate_short_term_crypto_materials(account); } omemo_ctx.sessions_keyfile = g_key_file_new(); if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { - int i; - char **groups = g_key_file_get_groups(omemo_ctx.sessions_keyfile, NULL); - for (i = 0; groups[i] != NULL; i++) { - int j; - GHashTable *device_store = NULL; - - device_store = g_hash_table_lookup(omemo_ctx.session_store, groups[i]); - if (!device_store) { - device_store = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); - g_hash_table_insert(omemo_ctx.session_store, groups[i], device_store); - } - - char **keys = g_key_file_get_keys(omemo_ctx.sessions_keyfile, groups[i], NULL, NULL); - for (j = 0; keys[j] != NULL; j++) { - uint32_t id = strtoul(keys[j], NULL, 10); - char *record_b64 = g_key_file_get_string(omemo_ctx.sessions_keyfile, groups[i], keys[j], NULL); - size_t record_len; - unsigned char *record = g_base64_decode(record_b64, &record_len); - signal_buffer *buffer = signal_buffer_create(record, record_len); - g_hash_table_insert(device_store, GINT_TO_POINTER(id), buffer); - } - } + load_sessions(); } } void omemo_generate_crypto_materials(ProfAccount *account) { - GError *error = NULL; - if (loaded) { return; } @@ -268,9 +225,7 @@ omemo_generate_crypto_materials(ProfAccount *account) g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, identity_key_private); g_free(identity_key_private); - if (!g_key_file_save_to_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, &error)) { - log_error("Error saving OMEMO identity to: %s, %s", omemo_ctx.identity_filename->str, error->message); - } + omemo_identity_keyfile_save(); omemo_generate_short_term_crypto_materials(account); } @@ -426,6 +381,22 @@ omemo_set_device_list(const char *const jid, GList * device_list) free(barejid); } +GKeyFile * +omemo_identity_keyfile(void) +{ + return omemo_ctx.identity_keyfile; +} + +void +omemo_identity_keyfile_save(void) +{ + GError *error = NULL; + + if (!g_key_file_save_to_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, &error)) { + log_error("Error saving OMEMO identity to: %s, %s", omemo_ctx.identity_filename->str, error->message); + } +} + GKeyFile * omemo_sessions_keyfile(void) { @@ -802,3 +773,73 @@ free_omemo_key(omemo_key_t *key) free((void *)key->data); free(key); } + +static void +load_identity(void) +{ + omemo_ctx.device_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, NULL); + omemo_ctx.registration_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, NULL); + + char *identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, NULL); + size_t identity_key_public_len; + unsigned char *identity_key_public = g_base64_decode(identity_key_public_b64, &identity_key_public_len); + omemo_ctx.identity_key_store.public = signal_buffer_create(identity_key_public, identity_key_public_len); + + char *identity_key_private_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, NULL); + size_t identity_key_private_len; + unsigned char *identity_key_private = g_base64_decode(identity_key_private_b64, &identity_key_private_len); + omemo_ctx.identity_key_store.private = signal_buffer_create(identity_key_private, identity_key_private_len); + signal_buffer_create(identity_key_private, identity_key_private_len); + + ec_public_key *public_key; + curve_decode_point(&public_key, identity_key_public, identity_key_public_len, omemo_ctx.signal); + ec_private_key *private_key; + curve_decode_private_point(&private_key, identity_key_private, identity_key_private_len, omemo_ctx.signal); + ratchet_identity_key_pair_create(&omemo_ctx.identity_key_pair, public_key, private_key); + + g_free(identity_key_public); + g_free(identity_key_private); + + char **keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, NULL, NULL); + if (keys) { + int i; + for (i = 0; keys[i] != NULL; i++) { + char *key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, keys[i], NULL); + size_t key_len; + unsigned char *key = g_base64_decode(key_b64, &key_len); + signal_buffer *buffer = signal_buffer_create(key, key_len); + g_hash_table_insert(omemo_ctx.identity_key_store.trusted, keys[i], buffer); + free(key_b64); + } + } +} + +static void +load_sessions(void) +{ + int i; + char **groups = g_key_file_get_groups(omemo_ctx.sessions_keyfile, NULL); + if (groups) { + for (i = 0; groups[i] != NULL; i++) { + int j; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(omemo_ctx.session_store, groups[i]); + if (!device_store) { + device_store = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); + g_hash_table_insert(omemo_ctx.session_store, groups[i], device_store); + } + + char **keys = g_key_file_get_keys(omemo_ctx.sessions_keyfile, groups[i], NULL, NULL); + for (j = 0; keys[j] != NULL; j++) { + uint32_t id = strtoul(keys[j], NULL, 10); + char *record_b64 = g_key_file_get_string(omemo_ctx.sessions_keyfile, groups[i], keys[j], NULL); + size_t record_len; + unsigned char *record = g_base64_decode(record_b64, &record_len); + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(device_store, GINT_TO_POINTER(id), buffer); + free(record_b64); + } + } + } +} diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 88c6d27c..72d3d3aa 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -26,6 +26,8 @@ void omemo_signed_prekey(unsigned char **output, size_t *length); void omemo_signed_prekey_signature(unsigned char **output, size_t *length); void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); +GKeyFile *omemo_identity_keyfile(void); +void omemo_identity_keyfile_save(void); GKeyFile *omemo_sessions_keyfile(void); void omemo_sessions_keyfile_save(void); char *omemo_fingerprint(const unsigned char *const identity_key_public, size_t len); diff --git a/src/omemo/store.c b/src/omemo/store.c index eee58e19..621c902c 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -26,7 +26,7 @@ signed_pre_key_store_new(void) void identity_key_store_new(identity_key_store_t *identity_key_store) { - identity_key_store->identity_key_store = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)signal_buffer_free); + identity_key_store->trusted = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)signal_buffer_free); identity_key_store->private = NULL; identity_key_store->public = NULL; } @@ -279,7 +279,13 @@ save_identity(const signal_protocol_address *address, uint8_t *key_data, char *node = g_strdup_printf("%s:%d", address->name, address->device_id); signal_buffer *buffer = signal_buffer_create(key_data, key_len); - g_hash_table_insert(identity_key_store->identity_key_store, node, buffer); + g_hash_table_insert(identity_key_store->trusted, strdup(node), buffer); + + char *key_b64 = g_base64_encode(key_data, key_len); + g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_TRUST, node, key_b64); + + omemo_identity_keyfile_save(); + free(node); return SG_SUCCESS; } @@ -292,9 +298,9 @@ is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, char *node = g_strdup_printf("%s:%d", address->name, address->device_id); signal_buffer *buffer = signal_buffer_create(key_data, key_len); - signal_buffer *original = g_hash_table_lookup(identity_key_store->identity_key_store, node); + signal_buffer *original = g_hash_table_lookup(identity_key_store->trusted, node); - return original == NULL || signal_buffer_compare(buffer, original) == 0; + return original != NULL && signal_buffer_compare(buffer, original) == 0; } int diff --git a/src/omemo/store.h b/src/omemo/store.h index 986b7c42..03800f84 100644 --- a/src/omemo/store.h +++ b/src/omemo/store.h @@ -3,6 +3,7 @@ #include "config.h" #define OMEMO_STORE_GROUP_IDENTITY "identity" +#define OMEMO_STORE_GROUP_TRUST "trust" #define OMEMO_STORE_KEY_DEVICE_ID "device_id" #define OMEMO_STORE_KEY_REGISTRATION_ID "registration_id" #define OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC "identity_key_public" @@ -12,7 +13,7 @@ typedef struct { signal_buffer *public; signal_buffer *private; uint32_t registration_id; - GHashTable * identity_key_store; + GHashTable *trusted; } identity_key_store_t; GHashTable * session_store_new(void); -- cgit 1.4.1-2-gfad0 From 063a5d1c52862065a62b07ec96ca3cde2b4d2955 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 7 Mar 2019 19:45:11 +0140 Subject: Add trust command --- src/command/cmd_ac.c | 1 + src/command/cmd_defs.c | 7 +- src/command/cmd_funcs.c | 58 +++++++++++- src/command/cmd_funcs.h | 1 + src/omemo/omemo.c | 244 ++++++++++++++++++++++++++++++++++++++++-------- src/omemo/omemo.h | 4 +- 6 files changed, 268 insertions(+), 47 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 55f05c7c..ce66d507 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -579,6 +579,7 @@ cmd_ac_init(void) omemo_ac = autocomplete_new(); autocomplete_add(omemo_ac, "gen"); autocomplete_add(omemo_ac, "start"); + autocomplete_add(omemo_ac, "trust"); autocomplete_add(omemo_ac, "fingerprint"); connect_property_ac = autocomplete_new(); diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 7b2df39b..200a46d3 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2331,10 +2331,11 @@ static struct cmd_t command_defs[] = }, { "/omemo", - parse_args, 1, 2, NULL, + parse_args, 1, 3, NULL, CMD_SUBFUNCS( { "gen", cmd_omemo_gen }, { "start", cmd_omemo_start }, + { "trust", cmd_omemo_trust }, { "fingerprint", cmd_omemo_fingerprint }) CMD_NOMAINFUNC CMD_TAGS( @@ -2343,6 +2344,7 @@ static struct cmd_t command_defs[] = CMD_SYN( "/omemo gen", "/omemo start []", + "/omemo trust [] ", "/omemo fingerprint") CMD_DESC( "Omemo commands to manage keys, and perform encryption during chat sessions.") @@ -2352,7 +2354,8 @@ static struct cmd_t command_defs[] = { "fingerprint", "Show current device fingerprint." }) CMD_EXAMPLES( "/omemo gen", - "/omemo start buddy@buddychat.org") + "/omemo start buddy@buddychat.org", + "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a") }, }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 70940f17..63c3df1b 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8003,12 +8003,60 @@ cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args) return TRUE; } - char *fingerprint = omemo_own_fingerprint(); - char *formated_fingerprint = omemo_format_fingerprint(fingerprint); - cons_show("%s", formated_fingerprint); - + char *fingerprint = omemo_own_fingerprint(TRUE); + cons_show("%s", fingerprint); free(fingerprint); - free(formated_fingerprint); + + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} + +gboolean +cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OMEMO information."); + return TRUE; + } + + if (!args[1]) { + cons_bad_cmd_usage(command); + return TRUE; + } + + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); + return TRUE; + } + + char *fingerprint; + char *barejid; + + /* Contact not provided */ + if (!args[2]) { + fingerprint = args[1]; + + if (window->type != WIN_CHAT) { + win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to trust a device without providing the contact."); + return TRUE; + } + + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + barejid = chatwin->barejid; + } else { + char *contact = args[2]; + barejid = roster_barejid_from_name(contact); + if (barejid == NULL) { + barejid = contact; + } + } + + omemo_trust(barejid, fingerprint); return TRUE; #else diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 4809f703..8a11881b 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -217,5 +217,6 @@ gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index d649ff7e..3cae3315 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -12,15 +12,16 @@ #include #include "config/account.h" +#include "config/files.h" #include "log.h" -#include "omemo/store.h" #include "omemo/crypto.h" #include "omemo/omemo.h" +#include "omemo/store.h" #include "ui/ui.h" -#include "xmpp/xmpp.h" +#include "ui/window_list.h" #include "xmpp/connection.h" #include "xmpp/omemo.h" -#include "config/files.h" +#include "xmpp/xmpp.h" static gboolean loaded; @@ -33,6 +34,10 @@ static void omemo_log(int level, const char *message, size_t len, void *user_dat static gboolean handle_own_device_list(const char *const jid, GList *device_list); static gboolean handle_device_list_start_session(const char *const jid, GList *device_list); static void free_omemo_key(omemo_key_t *key); +static char * omemo_fingerprint(ec_public_key *identity, gboolean formatted); +static unsigned char *omemo_fingerprint_decode(const char *const fingerprint, size_t *len); +static void cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity); +static void g_hash_table_free(GHashTable *hash_table); typedef gboolean (*OmemoDeviceListHandler)(const char *const jid, GList *device_list); @@ -56,6 +61,7 @@ struct omemo_context_t { GKeyFile *identity_keyfile; GString *sessions_filename; GKeyFile *sessions_keyfile; + GHashTable *known_devices; }; static omemo_context omemo_ctx; @@ -63,7 +69,7 @@ static omemo_context omemo_ctx; void omemo_init(void) { - log_info("Initialising OMEMO"); + log_info("OMEMO: initialising"); signal_crypto_provider crypto_provider = { .random_func = omemo_random_func, .hmac_sha256_init_func = omemo_hmac_sha256_init_func, @@ -155,6 +161,7 @@ omemo_init(void) loaded = 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_free); } void @@ -182,9 +189,9 @@ omemo_on_connect(ProfAccount *account) if (res == -1) { char *errmsg = strerror(errno); if (errmsg) { - log_error("Error creating directory: %s, %s", basedir->str, errmsg); + log_error("OMEMO: error creating directory: %s, %s", basedir->str, errmsg); } else { - log_error("Error creating directory: %s", basedir->str); + log_error("OMEMO: creating directory: %s", basedir->str); } } @@ -270,9 +277,10 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) void omemo_start_session(const char *const barejid) { - log_info("Start OMEMO session with %s", barejid); + log_info("OMEMO: start session with %s", barejid); GList *device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid); if (!device_list) { + log_info("OMEMO: missing device list for %s", barejid); omemo_devicelist_request(barejid); g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), handle_device_list_start_session); return; @@ -393,7 +401,7 @@ omemo_identity_keyfile_save(void) GError *error = NULL; if (!g_key_file_save_to_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, &error)) { - log_error("Error saving OMEMO identity to: %s, %s", omemo_ctx.identity_filename->str, error->message); + log_error("OMEMO: error saving identity to: %s, %s", omemo_ctx.identity_filename->str, error->message); } } @@ -409,7 +417,7 @@ omemo_sessions_keyfile_save(void) GError *error = NULL; if (!g_key_file_save_to_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, &error)) { - log_error("Error saving OMEMO sessions to: %s, %s", omemo_ctx.sessions_filename->str, error->message); + log_error("OMEMO: error saving sessions to: %s, %s", omemo_ctx.sessions_filename->str, error->message); } } @@ -426,8 +434,26 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, .device_id = device_id, }; + ec_public_key *identity_key; + curve_decode_point(&identity_key, identity_key_raw, identity_key_len, omemo_ctx.signal); + cache_device_identity(jid, device_id, identity_key); + + gboolean trusted = is_trusted_identity(&address, (uint8_t *)identity_key_raw, identity_key_len, &omemo_ctx.identity_key_store); + + ProfChatWin *chatwin = wins_get_chat(jid); + if (chatwin) { + char *fingerprint = omemo_fingerprint(identity_key, TRUE); + + win_println((ProfWin *)chatwin, THEME_DEFAULT, '-', "Available device identity: %s%s", fingerprint, trusted ? " (trusted)" : ""); + free(fingerprint); + } + + if (!trusted) { + goto out; + } + if (!contains_session(&address, omemo_ctx.session_store)) { - log_info("Create OMEMO session with %s device %d", jid, device_id); + int res; session_pre_key_bundle *bundle; signal_protocol_address *address; @@ -437,7 +463,11 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, address->device_id = device_id; session_builder *builder; - session_builder_create(&builder, omemo_ctx.store, address, omemo_ctx.signal); + res = session_builder_create(&builder, omemo_ctx.store, address, omemo_ctx.signal); + if (res != 0) { + log_error("OMEMO: cannot create session builder for %s device %d", jid, device_id); + goto out; + } int prekey_index; gcry_randomize(&prekey_index, sizeof(int), GCRY_STRONG_RANDOM); @@ -448,14 +478,24 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, curve_decode_point(&prekey_public, prekey->data, prekey->length, omemo_ctx.signal); ec_public_key *signed_prekey; curve_decode_point(&signed_prekey, signed_prekey_raw, signed_prekey_len, omemo_ctx.signal); - ec_public_key *identity_key; - curve_decode_point(&identity_key, identity_key_raw, identity_key_len, omemo_ctx.signal); - session_pre_key_bundle_create(&bundle, 0, device_id, prekey->id, prekey_public, signed_prekey_id, signed_prekey, signature, signature_len, identity_key); - session_builder_process_pre_key_bundle(builder, bundle); + res = session_pre_key_bundle_create(&bundle, 0, device_id, prekey->id, prekey_public, signed_prekey_id, signed_prekey, signature, signature_len, identity_key); + if (res != 0) { + log_error("OMEMO: cannot create pre key bundle for %s device %d", jid, device_id); + goto out; + } - g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); + res = session_builder_process_pre_key_bundle(builder, bundle); + if (res != 0) { + log_error("OMEMO: cannot process pre key bundle for %s device %d", jid, device_id); + goto out; + } + + log_info("OMEMO: create session with %s device %d", jid, device_id); } + +out: + g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); } gboolean @@ -492,6 +532,7 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean res = aes128gcm_encrypt(ciphertext, &ciphertext_len, tag, &tag_len, (const unsigned char * const)message, strlen(message), iv, key); if (res != 0) { + log_error("OMEMO: cannot encrypt message"); return FALSE; } @@ -505,16 +546,20 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean ciphertext_message *ciphertext; session_cipher *cipher; signal_protocol_address address = { - chatwin->barejid, strlen(chatwin->barejid), GPOINTER_TO_INT(device_ids_iter->data) + .name = chatwin->barejid, + .name_len = strlen(chatwin->barejid), + .device_id = GPOINTER_TO_INT(device_ids_iter->data) }; res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); if (res != 0) { + log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); continue; } res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); if (res != 0) { + log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); continue; } signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); @@ -531,16 +576,20 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean ciphertext_message *ciphertext; session_cipher *cipher; signal_protocol_address address = { - barejid, strlen(barejid), GPOINTER_TO_INT(device_ids_iter->data) + .name = barejid, + .name_len = strlen(barejid), + .device_id = GPOINTER_TO_INT(device_ids_iter->data) }; res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); if (res != 0) { + log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); continue; } res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); if (res != 0) { + log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); continue; } signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); @@ -583,21 +632,26 @@ omemo_on_message_recv(const char *const from, uint32_t sid, } if (!key) { + log_warning("OMEMO: Received a message with no corresponding key"); return NULL; } session_cipher *cipher; signal_buffer *plaintext_key; signal_protocol_address address = { - from, strlen(from), sid + .name = from, + .name_len = strlen(from), + .device_id = sid }; res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); if (res != 0) { + log_error("OMEMO: cannot create session cipher"); return NULL; } if (key->prekey) { + log_debug("OMEMO: decrypting message with prekey"); pre_key_signal_message *message; pre_key_signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); @@ -618,11 +672,13 @@ omemo_on_message_recv(const char *const from, uint32_t sid, /* Try to decrypt message anyway, it will fail */ res = session_cipher_decrypt_pre_key_signal_message(cipher, message, NULL, &plaintext_key); } else { + log_debug("OMEMO: decrypting message with existing session"); signal_message *message; signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); res = session_cipher_decrypt_signal_message(cipher, message, NULL, &plaintext_key); } if (res != 0) { + log_debug("OMEMO: cannot to decrypt message key"); return NULL; } @@ -647,13 +703,12 @@ omemo_on_message_recv(const char *const from, uint32_t sid, plaintext[plaintext_len] = '\0'; return (char *)plaintext; - } char * omemo_format_fingerprint(const char *const fingerprint) { - char *output = malloc(strlen(fingerprint) + strlen(fingerprint) / 8 + 1); + char *output = malloc(strlen(fingerprint) + strlen(fingerprint) / 8); int i, j; for (i = 0, j = 0; i < strlen(fingerprint); i++) { @@ -663,42 +718,134 @@ omemo_format_fingerprint(const char *const fingerprint) output[j++] = fingerprint[i]; } - output[strlen(fingerprint) + strlen(fingerprint) / 8] = '\0'; + output[j] = '\0'; return output; } char * -omemo_own_fingerprint() +omemo_own_fingerprint(gboolean formatted) { - signal_buffer *public = omemo_ctx.identity_key_store.public; - /* Skip first byte corresponding to signal base type */ - return omemo_fingerprint(signal_buffer_data(public) + 1, signal_buffer_len(public) - 1); + ec_public_key *identity = ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair); + return omemo_fingerprint(identity, formatted); } -char * -omemo_fingerprint(const unsigned char *const identity_key_public, size_t len) +static char * +omemo_fingerprint(ec_public_key *identity, gboolean formatted) { int i; - char *fingerprint = malloc(len * 2 + 1); + signal_buffer *identity_public_key; + + ec_public_key_serialize(&identity_public_key, identity); + size_t identity_public_key_len = signal_buffer_len(identity_public_key); + unsigned char *identity_public_key_data = signal_buffer_data(identity_public_key); + + /* Skip first byte corresponding to signal DJB_TYPE */ + identity_public_key_len--; + identity_public_key_data = &identity_public_key_data[1]; - for (i = 0; i < len; i++) { - fingerprint[i * 2] = (identity_key_public[i] & 0xf0) >> 4; - fingerprint[i * 2] += 0x30; - if (fingerprint[i * 2] > 0x39) { + char *fingerprint = malloc(identity_public_key_len * 2 + 1); + + for (i = 0; i < identity_public_key_len; i++) { + fingerprint[i * 2] = (identity_public_key_data[i] & 0xf0) >> 4; + fingerprint[i * 2] += '0'; + if (fingerprint[i * 2] > '9') { fingerprint[i * 2] += 0x27; } - fingerprint[(i * 2) + 1] = identity_key_public[i] & 0x0f; - fingerprint[(i * 2) + 1] += 0x30; - if (fingerprint[(i * 2) + 1] > 0x39) { + fingerprint[(i * 2) + 1] = identity_public_key_data[i] & 0x0f; + fingerprint[(i * 2) + 1] += '0'; + if (fingerprint[(i * 2) + 1] > '9') { fingerprint[(i * 2) + 1] += 0x27; } } - fingerprint[len * 2] = '\0'; + fingerprint[i * 2] = '\0'; + signal_buffer_free(identity_public_key); + + if (!formatted) { + return fingerprint; + } else { + char *formatted_fingerprint = omemo_format_fingerprint(fingerprint); + free(fingerprint); + return formatted_fingerprint; + } +} + +static unsigned char * +omemo_fingerprint_decode(const char *const fingerprint, size_t *len) +{ + unsigned char *output = malloc(strlen(fingerprint) / 2 + 1); + + int i; + int j; + for (i = 0, j = 0; i < strlen(fingerprint);) { + if (!g_ascii_isxdigit(fingerprint[i])) { + i++; + continue; + } + + output[j] = g_ascii_xdigit_value(fingerprint[i++]) << 4; + output[j] |= g_ascii_xdigit_value(fingerprint[i++]); + j++; + } + + *len = j; - return fingerprint; + return output; +} + +void +omemo_trust(const char *const jid, const char *const fingerprint_formatted) +{ + size_t len; + + GHashTable *known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid); + if (!known_identities) { + log_warning("OMEMO: cannot trust unknown device: %s", fingerprint_formatted); + cons_show("Cannot trust unknown device: %s", fingerprint_formatted); + return; + } + + /* Unformat fingerprint */ + char *fingerprint = malloc(strlen(fingerprint_formatted)); + int i; + int j; + for (i = 0, j = 0; fingerprint_formatted[i] != '\0'; i++) { + if (!g_ascii_isxdigit(fingerprint_formatted[i])) { + continue; + } + fingerprint[j++] = fingerprint_formatted[i]; + } + + fingerprint[j] = '\0'; + + uint32_t device_id = GPOINTER_TO_INT(g_hash_table_lookup(known_identities, fingerprint)); + free(fingerprint); + + if (!device_id) { + log_warning("OMEMO: cannot trust unknown device: %s", fingerprint_formatted); + cons_show("Cannot trust unknown device: %s", fingerprint_formatted); + return; + } + + /* TODO should not hardcode DJB_TYPE here + * should instead store identity key in known_identities along with + * device_id */ + signal_protocol_address address = { + .name = jid, + .name_len = strlen(jid), + .device_id = device_id, + }; + unsigned char *fingerprint_raw = omemo_fingerprint_decode(fingerprint_formatted, &len); + unsigned char djb_type[] = {'\x05'}; + signal_buffer *buffer = signal_buffer_create(djb_type, 1); + buffer = signal_buffer_append(buffer, fingerprint_raw, len); + save_identity(&address, signal_buffer_data(buffer), signal_buffer_len(buffer), &omemo_ctx.identity_key_store); + free(fingerprint_raw); + signal_buffer_free(buffer); + + omemo_bundle_request(jid, device_id, omemo_start_device_session_handle_bundle, free, strdup(jid)); } static void @@ -843,3 +990,24 @@ load_sessions(void) } } } + +static void +cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity) +{ + GHashTable *known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid); + if (!known_identities) { + known_identities = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); + g_hash_table_insert(omemo_ctx.known_devices, strdup(jid), known_identities); + } + + char *fingerprint = omemo_fingerprint(identity, FALSE); + log_info("OMEMO: cache identity for %s:%d: %s", jid, device_id, fingerprint); + g_hash_table_insert(known_identities, fingerprint, GINT_TO_POINTER(device_id)); +} + +static void +g_hash_table_free(GHashTable *hash_table) +{ + g_hash_table_remove_all(hash_table); + g_hash_table_unref(hash_table); +} diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 72d3d3aa..5be0e0cd 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -30,9 +30,9 @@ GKeyFile *omemo_identity_keyfile(void); void omemo_identity_keyfile_save(void); GKeyFile *omemo_sessions_keyfile(void); void omemo_sessions_keyfile_save(void); -char *omemo_fingerprint(const unsigned char *const identity_key_public, size_t len); char *omemo_format_fingerprint(const char *const fingerprint); -char *omemo_own_fingerprint(); +char *omemo_own_fingerprint(gboolean formatted); +void omemo_trust(const char *const jid, const char *const fingerprint); void omemo_start_session(const char *const barejid); 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); -- cgit 1.4.1-2-gfad0 From dbf96dcfc62d5bbfb1770a78a945d9822204d93f Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 13 Mar 2019 18:04:45 +0140 Subject: Allow to trust own device id --- src/command/cmd_funcs.c | 3 ++- src/omemo/omemo.c | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 63c3df1b..ba55243c 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8049,7 +8049,8 @@ cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args) assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); barejid = chatwin->barejid; } else { - char *contact = args[2]; + fingerprint = args[2]; + char *contact = args[1]; barejid = roster_barejid_from_name(contact); if (barejid == NULL) { barejid = contact; diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 3cae3315..b45e1834 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -440,6 +440,15 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, gboolean trusted = is_trusted_identity(&address, (uint8_t *)identity_key_raw, identity_key_len, &omemo_ctx.identity_key_store); + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *ownjid = xmpp_jid_bare(ctx, session_get_account_name()); + if (g_strcmp0(jid, ownjid) == 0) { + char *fingerprint = omemo_fingerprint(identity_key, TRUE); + + cons_show("Available device identity: %s%s", fingerprint, trusted ? " (trusted)" : ""); + free(fingerprint); + } + ProfChatWin *chatwin = wins_get_chat(jid); if (chatwin) { char *fingerprint = omemo_fingerprint(identity_key, TRUE); -- cgit 1.4.1-2-gfad0 From 36ce21fc6d8104ef2145afbe3056747e49032ca4 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 13 Mar 2019 18:39:31 +0140 Subject: Add store hints for OMEMO encrypted messages Store hints are required has some server might discard messages without body. Here we ensure OMEMO messages are stored on server and delivered to client when they connect back. It's really important since it avoid libsignal to desynchronize counters. --- src/xmpp/message.c | 2 ++ src/xmpp/stanza.c | 12 ++++++++++++ src/xmpp/stanza.h | 1 + 3 files changed, 15 insertions(+) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 6ca63b08..53190998 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -404,6 +404,8 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, stanza_attach_state(ctx, message, state); } + stanza_attach_hints_store(ctx, message); + if (request_receipt) { stanza_attach_receipt_request(ctx, message); } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 61086f77..2b2ec0d8 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -395,6 +395,18 @@ stanza_attach_hints_no_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza) return stanza; } +xmpp_stanza_t* +stanza_attach_hints_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *store = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(store, "store"); + xmpp_stanza_set_ns(store, STANZA_NS_HINTS); + xmpp_stanza_add_child(stanza, store); + xmpp_stanza_release(store); + + return stanza; +} + xmpp_stanza_t* stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index bc157e46..f392eeff 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -233,6 +233,7 @@ xmpp_stanza_t* stanza_attach_state(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const xmpp_stanza_t* stanza_attach_carbons_private(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); xmpp_stanza_t* stanza_attach_hints_no_copy(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); xmpp_stanza_t* stanza_attach_hints_no_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); +xmpp_stanza_t* stanza_attach_hints_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); xmpp_stanza_t* stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); xmpp_stanza_t* stanza_attach_x_oob_url(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const url); -- cgit 1.4.1-2-gfad0 From 2622c2d21a1f120cb39800b8c96b6e5ce916589b Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 13 Mar 2019 19:43:07 +0140 Subject: Add OMEMO untrust command --- src/command/cmd_ac.c | 1 + src/command/cmd_defs.c | 4 +++- src/command/cmd_funcs.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ src/command/cmd_funcs.h | 1 + src/omemo/omemo.c | 22 +++++++++++++++++++++ src/omemo/omemo.h | 1 + 6 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index ce66d507..588c8b4f 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -580,6 +580,7 @@ cmd_ac_init(void) autocomplete_add(omemo_ac, "gen"); autocomplete_add(omemo_ac, "start"); autocomplete_add(omemo_ac, "trust"); + autocomplete_add(omemo_ac, "untrust"); autocomplete_add(omemo_ac, "fingerprint"); connect_property_ac = autocomplete_new(); diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 200a46d3..70e4595e 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2336,6 +2336,7 @@ static struct cmd_t command_defs[] = { "gen", cmd_omemo_gen }, { "start", cmd_omemo_start }, { "trust", cmd_omemo_trust }, + { "untrust", cmd_omemo_untrust }, { "fingerprint", cmd_omemo_fingerprint }) CMD_NOMAINFUNC CMD_TAGS( @@ -2355,7 +2356,8 @@ static struct cmd_t command_defs[] = CMD_EXAMPLES( "/omemo gen", "/omemo start buddy@buddychat.org", - "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a") + "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", + "/omemo untrust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a") }, }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index ba55243c..572c0b96 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8065,3 +8065,55 @@ cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args) return TRUE; #endif } + +gboolean +cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OMEMO information."); + return TRUE; + } + + if (!args[1]) { + cons_bad_cmd_usage(command); + return TRUE; + } + + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); + return TRUE; + } + + char *fingerprint; + char *barejid; + + /* Contact not provided */ + if (!args[2]) { + fingerprint = args[1]; + + if (window->type != WIN_CHAT) { + win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to trust a device without providing the contact."); + return TRUE; + } + + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + barejid = chatwin->barejid; + } else { + fingerprint = args[2]; + char *contact = args[1]; + barejid = roster_barejid_from_name(contact); + if (barejid == NULL) { + barejid = contact; + } + } + + omemo_untrust(barejid, fingerprint); + + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 8a11881b..efa8cd83 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -218,5 +218,6 @@ gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args) gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index b45e1834..fceb0363 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -857,6 +857,28 @@ omemo_trust(const char *const jid, const char *const fingerprint_formatted) omemo_bundle_request(jid, device_id, omemo_start_device_session_handle_bundle, free, strdup(jid)); } +void +omemo_untrust(const char *const jid, const char *const fingerprint_formatted) +{ + size_t len; + unsigned char *fingerprint = omemo_fingerprint_decode(fingerprint_formatted, &len); + + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, omemo_ctx.identity_key_store.trusted); + while (g_hash_table_iter_next(&iter, &key, &value)) { + signal_buffer *buffer = value; + unsigned char *original = signal_buffer_data(buffer); + /* Skip DJB_TYPE byte */ + original++; + if ((signal_buffer_len(buffer) - 1) == len && memcmp(original, fingerprint, len) == 0) { + g_hash_table_remove(omemo_ctx.identity_key_store.trusted, key); + } + } + free(fingerprint); +} + static void lock(void *user_data) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 5be0e0cd..7f9b7992 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -33,6 +33,7 @@ void omemo_sessions_keyfile_save(void); char *omemo_format_fingerprint(const char *const fingerprint); char *omemo_own_fingerprint(gboolean formatted); void omemo_trust(const char *const jid, const char *const fingerprint); +void omemo_untrust(const char *const jid, const char *const fingerprint); void omemo_start_session(const char *const barejid); 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); -- cgit 1.4.1-2-gfad0 From 89ece4f23558f700940264c1815dc8b3977c81ca Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 13 Mar 2019 21:46:33 +0220 Subject: Add UI feedback for OMEMO gen command --- src/command/cmd_funcs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 572c0b96..07c8d3f1 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7898,6 +7898,7 @@ cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args) ProfAccount *account = accounts_get_account(session_get_account_name()); omemo_generate_crypto_materials(account); + cons_show("OMEMO crytographic materials generated."); return TRUE; #else cons_show("This version of Profanity has not been built with OMEMO support enabled"); -- cgit 1.4.1-2-gfad0 From 5d90199a41dc53259f5bacd42d59155d09a1a116 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 13 Mar 2019 19:50:43 +0140 Subject: Avoid opening win if crypto materials are missing --- src/command/cmd_funcs.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 07c8d3f1..01690867 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7915,6 +7915,11 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) return TRUE; } + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); + return TRUE; + } + // recipient supplied if (args[1]) { char *contact = args[1]; @@ -7944,11 +7949,6 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) return TRUE; } - if (!omemo_loaded()) { - win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); - return TRUE; - } - omemo_start_session(barejid); chatwin->is_omemo = TRUE; } else { @@ -7974,11 +7974,6 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) return TRUE; } - if (!omemo_loaded()) { - win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); - return TRUE; - } - omemo_start_session(chatwin->barejid); chatwin->is_omemo = TRUE; } -- cgit 1.4.1-2-gfad0 From 2455bcc28a5988235dff6fccc6a4fb494db936a2 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 13 Mar 2019 20:09:45 +0140 Subject: Avoid using xmpp_jid_bare --- src/omemo/omemo.c | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index fceb0363..f360232f 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -167,12 +167,10 @@ omemo_init(void) void omemo_on_connect(ProfAccount *account) { - xmpp_ctx_t * const ctx = connection_get_ctx(); - char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); char *omemodir = files_get_data_path(DIR_OMEMO); GString *basedir = g_string_new(omemodir); free(omemodir); - gchar *account_dir = str_replace(barejid, "@", "_at_"); + gchar *account_dir = str_replace(account->jid, "@", "_at_"); g_string_append(basedir, "/"); g_string_append(basedir, account_dir); g_string_append(basedir, "/"); @@ -265,11 +263,8 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) /* Ensure we get our current device list, and it gets updated with our * device_id */ - xmpp_ctx_t * const ctx = connection_get_ctx(); - char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); - g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), handle_own_device_list); - omemo_devicelist_request(barejid); - free(barejid); + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(account->jid), handle_own_device_list); + omemo_devicelist_request(account->jid); omemo_bundle_publish(); } @@ -367,26 +362,25 @@ omemo_prekeys(GList **prekeys, GList **ids, GList **lengths) } void -omemo_set_device_list(const char *const jid, GList * device_list) +omemo_set_device_list(const char *const from, GList * device_list) { - xmpp_ctx_t * const ctx = connection_get_ctx(); - char *barejid = xmpp_jid_bare(ctx, jid); + Jid *jid = jid_create(from); - g_hash_table_insert(omemo_ctx.device_list, strdup(barejid), device_list); - if (strchr(barejid, '@') == NULL) { + g_hash_table_insert(omemo_ctx.device_list, strdup(jid->barejid), device_list); + if (strchr(jid->barejid, '@') == NULL) { // barejid is server so we should handle it as our own device list - g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), handle_own_device_list); + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(jid->barejid), handle_own_device_list); } - OmemoDeviceListHandler handler = g_hash_table_lookup(omemo_ctx.device_list_handler, barejid); + OmemoDeviceListHandler handler = g_hash_table_lookup(omemo_ctx.device_list_handler, jid->barejid); if (handler) { - gboolean keep = handler(barejid, device_list); + gboolean keep = handler(jid->barejid, device_list); if (!keep) { - g_hash_table_remove(omemo_ctx.device_list_handler, barejid); + g_hash_table_remove(omemo_ctx.device_list_handler, jid->barejid); } } - free(barejid); + free(jid); } GKeyFile * @@ -440,9 +434,8 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, gboolean trusted = is_trusted_identity(&address, (uint8_t *)identity_key_raw, identity_key_len, &omemo_ctx.identity_key_store); - xmpp_ctx_t * const ctx = connection_get_ctx(); - char *ownjid = xmpp_jid_bare(ctx, session_get_account_name()); - if (g_strcmp0(jid, ownjid) == 0) { + Jid *ownjid = jid_create(session_get_account_name()); + if (g_strcmp0(jid, ownjid->barejid) == 0) { char *fingerprint = omemo_fingerprint(identity_key, TRUE); cons_show("Available device identity: %s%s", fingerprint, trusted ? " (trusted)" : ""); @@ -504,22 +497,22 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, } out: - g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); + g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); + free(ownjid); } gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) { int res; - xmpp_ctx_t * const ctx = connection_get_ctx(); - char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); + Jid *jid = jid_create(session_get_account_name()); GList *recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, chatwin->barejid); if (!recipient_device_id) { return FALSE; } - GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, barejid); + GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid); /* TODO generate fresh AES-GCM materials */ /* TODO encrypt message */ @@ -585,8 +578,8 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean ciphertext_message *ciphertext; session_cipher *cipher; signal_protocol_address address = { - .name = barejid, - .name_len = strlen(barejid), + .name = jid->barejid, + .name_len = strlen(jid->barejid), .device_id = GPOINTER_TO_INT(device_ids_iter->data) }; -- cgit 1.4.1-2-gfad0 From 8952684472eac14967134279bcc39cbff7f55a7b Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 14 Mar 2019 08:34:22 +0100 Subject: Add hints about trust command when printing fingerprints --- src/omemo/omemo.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index f360232f..b8307352 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -438,7 +438,12 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, if (g_strcmp0(jid, ownjid->barejid) == 0) { char *fingerprint = omemo_fingerprint(identity_key, TRUE); - cons_show("Available device identity: %s%s", fingerprint, trusted ? " (trusted)" : ""); + cons_show("Available device identity for %s: %s%s", ownjid->barejid, fingerprint, trusted ? " (trusted)" : ""); + if (trusted) { + cons_show("You can untrust it with '/omemo untrust %s '", ownjid->barejid); + } else { + cons_show("You can trust it with '/omemo trust %s '", ownjid->barejid); + } free(fingerprint); } @@ -447,6 +452,11 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, char *fingerprint = omemo_fingerprint(identity_key, TRUE); win_println((ProfWin *)chatwin, THEME_DEFAULT, '-', "Available device identity: %s%s", fingerprint, trusted ? " (trusted)" : ""); + if (trusted) { + win_println((ProfWin *)chatwin, THEME_DEFAULT, '-', "You can untrust it with '/omemo untrust '"); + } else { + win_println((ProfWin *)chatwin, THEME_DEFAULT, '-', "You can trust it with '/omemo trust '"); + } free(fingerprint); } -- cgit 1.4.1-2-gfad0 From 4826da3aa6ac36208b35e0988b14ea7f767f7549 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 14 Mar 2019 06:00:25 +0140 Subject: Don't rely on account name being jid --- src/omemo/omemo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index b8307352..6f22a50a 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -434,7 +434,7 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, gboolean trusted = is_trusted_identity(&address, (uint8_t *)identity_key_raw, identity_key_len, &omemo_ctx.identity_key_store); - Jid *ownjid = jid_create(session_get_account_name()); + Jid *ownjid = jid_create(connection_get_fulljid()); if (g_strcmp0(jid, ownjid->barejid) == 0) { char *fingerprint = omemo_fingerprint(identity_key, TRUE); @@ -515,7 +515,7 @@ gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) { int res; - Jid *jid = jid_create(session_get_account_name()); + Jid *jid = jid_create(connection_get_fulljid()); GList *recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, chatwin->barejid); if (!recipient_device_id) { -- cgit 1.4.1-2-gfad0 From 197e098f4b336945d0e76f31a74fc671589afcd1 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 14 Mar 2019 09:57:57 +0100 Subject: Log device_id on startup --- src/omemo/omemo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 6f22a50a..4937c4e6 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -214,6 +214,7 @@ omemo_generate_crypto_materials(ProfAccount *account) gcry_randomize(&omemo_ctx.device_id, 4, GCRY_VERY_STRONG_RANDOM); omemo_ctx.device_id &= 0x7fffffff; + log_info("OMEMO: device id: %d", omemo_ctx.device_id); signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); @@ -959,6 +960,7 @@ static void load_identity(void) { omemo_ctx.device_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, NULL); + log_info("OMEMO: device id: %d", omemo_ctx.device_id); omemo_ctx.registration_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, NULL); char *identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, NULL); -- cgit 1.4.1-2-gfad0 From 6bb7deeb9c33c61a1bb880dd15d45185e27a48d5 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 14 Mar 2019 06:42:59 +0140 Subject: Add logs about crypto generation --- src/omemo/omemo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 4937c4e6..5916ee8f 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -212,6 +212,7 @@ omemo_generate_crypto_materials(ProfAccount *account) return; } + log_info("Generate long term OMEMO cryptography metarials"); gcry_randomize(&omemo_ctx.device_id, 4, GCRY_VERY_STRONG_RANDOM); omemo_ctx.device_id &= 0x7fffffff; log_info("OMEMO: device id: %d", omemo_ctx.device_id); @@ -241,6 +242,8 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) { unsigned int start; + log_info("Generate short term OMEMO cryptography metarials"); + gcry_randomize(&start, sizeof(unsigned int), GCRY_VERY_STRONG_RANDOM); signal_protocol_key_helper_pre_key_list_node *pre_keys_head; signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start, 100, omemo_ctx.signal); @@ -959,6 +962,7 @@ free_omemo_key(omemo_key_t *key) static void load_identity(void) { + log_info("Loading OMEMO identity"); omemo_ctx.device_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, NULL); log_info("OMEMO: device id: %d", omemo_ctx.device_id); omemo_ctx.registration_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, NULL); -- cgit 1.4.1-2-gfad0 From 0dd5441f923d2b9a9e29e3b083dc3a4f61007302 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 14 Mar 2019 19:43:14 +0140 Subject: device_list iq should be from barejid or none Ensure we request device_list and remove non conforming handling of responses. Move initialisation of iq_handlers before call to sv_ev_login_account_success --- src/omemo/omemo.c | 13 ++++++++----- src/xmpp/omemo.c | 27 +++++++++++---------------- src/xmpp/session.c | 18 ++++++------------ 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 5916ee8f..7850dad9 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -203,6 +203,8 @@ omemo_on_connect(ProfAccount *account) if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { load_sessions(); } + + omemo_devicelist_subscribe(); } void @@ -368,13 +370,14 @@ omemo_prekeys(GList **prekeys, GList **ids, GList **lengths) void omemo_set_device_list(const char *const from, GList * device_list) { - Jid *jid = jid_create(from); + Jid *jid; + if (from) { + jid = jid_create(from); + } else { + jid = jid_create(connection_get_fulljid()); + } g_hash_table_insert(omemo_ctx.device_list, strdup(jid->barejid), device_list); - if (strchr(jid->barejid, '@') == NULL) { - // barejid is server so we should handle it as our own device list - g_hash_table_insert(omemo_ctx.device_list_handler, strdup(jid->barejid), handle_own_device_list); - } OmemoDeviceListHandler handler = g_hash_table_lookup(omemo_ctx.device_list_handler, jid->barejid); if (handler) { diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index f68f7d6e..0c033921 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -74,7 +74,7 @@ void omemo_bundle_request(const char * const jid, uint32_t device_id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata) { xmpp_ctx_t * const ctx = connection_get_ctx(); - char *id = connection_create_stanza_id("devicelist_request"); + char *id = connection_create_stanza_id("bundle_request"); xmpp_stanza_t *iq = stanza_create_omemo_bundle_request(ctx, id, jid, device_id); iq_id_handler_add(id, func, free_func, userdata); @@ -278,9 +278,6 @@ _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) { GList *device_list = NULL; const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); - if (!from) { - return 1; - } xmpp_stanza_t *root = NULL; xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); @@ -303,19 +300,17 @@ _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) } xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); - if (!item) { - return 1; - } - - xmpp_stanza_t *list = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO); - if (!list) { - return 1; - } + if (item) { + xmpp_stanza_t *list = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO); + if (!list) { + return 1; + } - xmpp_stanza_t *device; - for (device = xmpp_stanza_get_children(list); device != NULL; device = xmpp_stanza_get_next(device)) { - const char *id = xmpp_stanza_get_id(device); - device_list = g_list_append(device_list, GINT_TO_POINTER(strtoul(id, NULL, 10))); + xmpp_stanza_t *device; + for (device = xmpp_stanza_get_children(list); device != NULL; device = xmpp_stanza_get_next(device)) { + const char *id = xmpp_stanza_get_id(device); + device_list = g_list_append(device_list, GINT_TO_POINTER(strtoul(id, NULL, 10))); + } } omemo_set_device_list(from, device_list); diff --git a/src/xmpp/session.c b/src/xmpp/session.c index b05c5f7b..016fe76e 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -291,6 +291,12 @@ session_get_account_name(void) void session_login_success(gboolean secured) { + chat_sessions_init(); + + message_handlers_init(); + presence_handlers_init(); + iq_handlers_init(); + // logged in with account if (saved_account.name) { log_debug("Connection handler: logged in with account name: %s", saved_account.name); @@ -309,21 +315,9 @@ session_login_success(gboolean secured) _session_free_saved_details(); } - chat_sessions_init(); - - message_handlers_init(); - presence_handlers_init(); - iq_handlers_init(); - roster_request(); bookmark_request(); blocking_request(); -#ifdef HAVE_OMEMO - omemo_devicelist_subscribe(); - if (omemo_loaded()) { - /* TODO: update devicelist */ - } -#endif // items discovery char *domain = connection_get_domain(); -- cgit 1.4.1-2-gfad0 From 3e325a61c3feae8513cf61cb4d9620c499cb4c83 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 15 Mar 2019 07:38:05 +0140 Subject: Add support for OMEMO, OTR, PGP built together --- src/event/client_events.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/src/event/client_events.c b/src/event/client_events.c index 76a38b15..79ed88f6 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -145,9 +145,10 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo return; } -// OTR suported, PGP supported +// OTR suported, PGP supported, OMEMO unsupported #ifdef HAVE_LIBOTR #ifdef HAVE_LIBGPGME +#ifndef HAVE_OMEMO if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -168,10 +169,12 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo return; #endif #endif +#endif -// OTR supported, PGP unsupported +// OTR supported, PGP unsupported, OMEMO unsupported #ifdef HAVE_LIBOTR #ifndef HAVE_LIBGPGME +#ifndef HAVE_OMEMO gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); if (!handled) { char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); @@ -185,10 +188,12 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo return; #endif #endif +#endif -// OTR unsupported, PGP supported +// OTR unsupported, PGP supported, OMEMO unsupported #ifndef HAVE_LIBOTR #ifdef HAVE_LIBGPGME +#ifndef HAVE_OMEMO if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -206,7 +211,11 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo return; #endif #endif +#endif +// OTR unsupported, PGP unsupported, OMEMO supported +#ifndef HAVE_LIBOTR +#ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { omemo_on_message_send(chatwin, plugin_msg, request_receipt); @@ -221,10 +230,89 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo free(plugin_msg); return; #endif +#endif +#endif + +// OTR supported, PGP unsupported, OMEMO supported +#ifdef HAVE_LIBOTR +#ifndef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (chatwin->is_omemo) { + omemo_on_message_send(chatwin, plugin_msg, request_receipt); + } else { + gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); + if (!handled) { + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + chat_log_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); + free(id); + } + } + + plugins_post_chat_message_send(chatwin->barejid, plugin_msg); + free(plugin_msg); + return; +#endif +#endif +#endif -// OTR unsupported, PGP unsupported +// OTR unsupported, PGP supported, OMEMO supported +#ifndef HAVE_LIBOTR +#ifdef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (chatwin->is_omemo) { + omemo_on_message_send(chatwin, plugin_msg, request_receipt); + } else if (chatwin->pgp_send) { + char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); + chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt); + free(id); + } else { + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + chat_log_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); + free(id); + } + + plugins_post_chat_message_send(chatwin->barejid, plugin_msg); + free(plugin_msg); + return; +#endif +#endif +#endif + +// OTR supported, PGP supported, OMEMO supported +#ifdef HAVE_LIBOTR +#ifdef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (chatwin->is_omemo) { + omemo_on_message_send(chatwin, plugin_msg, request_receipt); + } else if (chatwin->pgp_send) { + char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); + chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt); + free(id); + } else { + gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); + if (!handled) { + char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); + chat_log_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); + free(id); + } + } + + plugins_post_chat_message_send(chatwin->barejid, plugin_msg); + free(plugin_msg); + return; +#endif +#endif +#endif + +// OTR unsupported, PGP unsupported, OMEMO unsupported #ifndef HAVE_LIBOTR #ifndef HAVE_LIBGPGME +#ifndef HAVE_OMEMO char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); chat_log_msg_out(chatwin->barejid, plugin_msg); chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); @@ -235,6 +323,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo return; #endif #endif +#endif } void -- cgit 1.4.1-2-gfad0 From 319fb856e0404f78153ed8a7dde464c1d92ca461 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 15 Mar 2019 07:48:08 +0140 Subject: Handle decrypted OMEMO messages as such --- src/event/server_events.c | 94 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index 33712328..7f293198 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -441,6 +441,16 @@ _sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, char *barejid, char } #endif +#ifdef HAVE_OMEMO +static void +_sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, GDateTime *timestamp) +{ + chatwin_incoming_msg(chatwin, resource, message, timestamp, new_win, PROF_MSG_OMEMO); + chat_log_omemo_msg_in(barejid, message, timestamp); + chatwin->pgp_recv = FALSE; +} +#endif + static void _sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, GDateTime *timestamp) { @@ -460,9 +470,10 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m new_win = TRUE; } -// OTR suported, PGP supported +// OTR suported, PGP supported, OMEMO unsupported #ifdef HAVE_LIBOTR #ifdef HAVE_LIBGPGME +#ifndef HAVE_OMEMO if (pgp_message) { if (chatwin->is_otr) { win_println((ProfWin*)chatwin, THEME_DEFAULT, '-', "PGP encrypted message received whilst in OTR session."); @@ -476,19 +487,23 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m return; #endif #endif +#endif -// OTR supported, PGP unsupported +// OTR supported, PGP unsupported, OMEMO unsupported #ifdef HAVE_LIBOTR #ifndef HAVE_LIBGPGME +#ifndef HAVE_OMEMO _sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp); rosterwin_roster(); return; #endif #endif +#endif -// OTR unsupported, PGP supported +// OTR unsupported, PGP supported, OMEMO unsupported #ifndef HAVE_LIBOTR #ifdef HAVE_LIBGPGME +#ifndef HAVE_OMEMO if (pgp_message) { _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp); } else { @@ -498,15 +513,86 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m return; #endif #endif +#endif + +// OTR suported, PGP supported, OMEMO supported +#ifdef HAVE_LIBOTR +#ifdef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (pgp_message) { + if (chatwin->is_otr) { + win_println((ProfWin*)chatwin, THEME_DEFAULT, '-', "PGP encrypted message received whilst in OTR session."); + } else { // PROF_ENC_NONE, PROF_ENC_PGP + _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp); + } + } else if (omemo) { + _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp); + } else { + _sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp); + } + rosterwin_roster(); + return; +#endif +#endif +#endif -// OTR unsupported, PGP unsupported +// OTR supported, PGP unsupported, OMEMO supported +#ifdef HAVE_LIBOTR +#ifndef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (omemo) { + _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp); + } else { + _sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp); + } + rosterwin_roster(); + return; +#endif +#endif +#endif + +// OTR unsupported, PGP supported, OMEMO supported +#ifndef HAVE_LIBOTR +#ifdef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (pgp_message) { + _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp); + } else if (omemo) { + _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp); + } else { + _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp); + } + rosterwin_roster(); + return; +#endif +#endif +#endif + +// OTR unsupported, PGP unsupported, OMEMO supported +#ifndef HAVE_LIBOTR +#ifndef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (omemo) { + _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp); + } else { + _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp); + } + rosterwin_roster(); + return; +#endif +#endif +#endif + +// OTR unsupported, PGP unsupported, OMEMO unsupported #ifndef HAVE_LIBOTR #ifndef HAVE_LIBGPGME +#ifndef HAVE_OMEMO _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp); rosterwin_roster(); return; #endif #endif +#endif } void -- cgit 1.4.1-2-gfad0 From 33b0b4642000166ca7f8033ac5ff2e2c6e7b4671 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 15 Mar 2019 18:47:43 +0140 Subject: Fix prekey handling after first use libsignal will remove prekey on its own. --- src/omemo/omemo.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 7850dad9..23892c02 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -675,21 +675,20 @@ omemo_on_message_recv(const char *const from, uint32_t sid, pre_key_signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); + res = session_cipher_decrypt_pre_key_signal_message(cipher, message, NULL, &plaintext_key); /* Replace used pre_key in bundle */ uint32_t pre_key_id = pre_key_signal_message_get_pre_key_id(message); - g_hash_table_remove(omemo_ctx.pre_key_store, GINT_TO_POINTER(pre_key_id)); ec_key_pair *ec_pair; session_pre_key *new_pre_key; curve_generate_key_pair(omemo_ctx.signal, &ec_pair); session_pre_key_create(&new_pre_key, pre_key_id, ec_pair); - g_hash_table_insert(omemo_ctx.pre_key_store, GINT_TO_POINTER(pre_key_id), new_pre_key); + signal_protocol_pre_key_store_key(omemo_ctx.store, new_pre_key); omemo_bundle_publish(); - /* Start a new session */ - omemo_bundle_request(from, sid, omemo_start_device_session_handle_bundle, free, strdup(from)); - - /* Try to decrypt message anyway, it will fail */ - res = session_cipher_decrypt_pre_key_signal_message(cipher, message, NULL, &plaintext_key); + if (res == 0) { + /* Start a new session */ + omemo_bundle_request(from, sid, omemo_start_device_session_handle_bundle, free, strdup(from)); + } } else { log_debug("OMEMO: decrypting message with existing session"); signal_message *message; @@ -697,7 +696,7 @@ omemo_on_message_recv(const char *const from, uint32_t sid, res = session_cipher_decrypt_signal_message(cipher, message, NULL, &plaintext_key); } if (res != 0) { - log_debug("OMEMO: cannot to decrypt message key"); + log_error("OMEMO: cannot decrypt message key"); return NULL; } -- cgit 1.4.1-2-gfad0 From a3897abba1e093aeab2b46bccded87485e518bb1 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 14 Mar 2019 22:05:47 +0220 Subject: Add MUC message decryption --- src/omemo/omemo.c | 58 +++++++++++++++++++++++++++++++++++++++++------------- src/omemo/omemo.h | 2 +- src/ui/win_types.h | 1 + src/xmpp/message.c | 12 ++++++++--- src/xmpp/omemo.c | 9 +++++---- 5 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 23892c02..62478e5d 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -20,6 +20,7 @@ #include "ui/ui.h" #include "ui/window_list.h" #include "xmpp/connection.h" +#include "xmpp/muc.h" #include "xmpp/omemo.h" #include "xmpp/xmpp.h" @@ -387,7 +388,7 @@ omemo_set_device_list(const char *const from, GList * device_list) } } - free(jid); + jid_destroy(jid); } GKeyFile * @@ -515,18 +516,20 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, out: g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); - free(ownjid); + jid_destroy(ownjid); } gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) { int res; + gboolean ret = FALSE; Jid *jid = jid_create(connection_get_fulljid()); + GList *keys = NULL; GList *recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, chatwin->barejid); if (!recipient_device_id) { - return FALSE; + goto out; } GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid); @@ -552,13 +555,12 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean res = aes128gcm_encrypt(ciphertext, &ciphertext_len, tag, &tag_len, (const unsigned char * const)message, strlen(message), iv, key); if (res != 0) { log_error("OMEMO: cannot encrypt message"); - return FALSE; + goto out; } memcpy(key_tag, key, AES128_GCM_KEY_LENGTH); memcpy(key_tag + AES128_GCM_KEY_LENGTH, tag, AES128_GCM_TAG_LENGTH); - GList *keys = NULL; GList *device_ids_iter; for (device_ids_iter = recipient_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { int res; @@ -623,7 +625,10 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean char *id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt); chat_log_omemo_msg_out(chatwin->barejid, message); chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OMEMO, request_receipt); + ret = TRUE; +out: + jid_destroy(jid); free(id); g_list_free_full(keys, free); free(ciphertext); @@ -632,14 +637,22 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean gcry_free(tag); gcry_free(key_tag); - return TRUE; + return ret; } char * -omemo_on_message_recv(const char *const from, uint32_t sid, +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) + const unsigned char *const payload, size_t payload_len, gboolean muc) { + unsigned char *plaintext = NULL; + Jid *sender; + Jid *from = jid_create(from_jid); + if (!from) { + log_error("Invalid jid %s", from_jid); + goto out; + } + int res; GList *key_iter; omemo_key_t *key = NULL; @@ -652,21 +665,35 @@ omemo_on_message_recv(const char *const from, uint32_t sid, if (!key) { log_warning("OMEMO: Received a message with no corresponding key"); - return NULL; + goto out; + } + + if (muc) { + GList *roster = muc_roster(from->barejid); + GList *iter; + for (iter = roster; iter != NULL; iter = iter->next) { + Occupant *occupant = (Occupant *)iter->data; + if (g_strcmp0(occupant->nick, from->resourcepart) == 0) { + sender = jid_create(occupant->jid); + break; + } + } + } else { + sender = jid_create(from->barejid); } session_cipher *cipher; signal_buffer *plaintext_key; signal_protocol_address address = { - .name = from, - .name_len = strlen(from), + .name = sender->barejid, + .name_len = strlen(sender->barejid), .device_id = sid }; res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); if (res != 0) { log_error("OMEMO: cannot create session cipher"); - return NULL; + goto out; } if (key->prekey) { @@ -687,7 +714,7 @@ omemo_on_message_recv(const char *const from, uint32_t sid, if (res == 0) { /* Start a new session */ - omemo_bundle_request(from, sid, omemo_start_device_session_handle_bundle, free, strdup(from)); + omemo_bundle_request(sender->barejid, sid, omemo_start_device_session_handle_bundle, free, strdup(sender->barejid)); } } else { log_debug("OMEMO: decrypting message with existing session"); @@ -707,7 +734,7 @@ omemo_on_message_recv(const char *const from, uint32_t sid, } size_t plaintext_len = payload_len; - unsigned char *plaintext = malloc(plaintext_len + 1); + plaintext = malloc(plaintext_len + 1); res = aes128gcm_decrypt(plaintext, &plaintext_len, payload, payload_len, iv, signal_buffer_data(plaintext_key), signal_buffer_data(plaintext_key) + AES128_GCM_KEY_LENGTH); @@ -720,6 +747,9 @@ omemo_on_message_recv(const char *const from, uint32_t sid, plaintext[plaintext_len] = '\0'; +out: + jid_destroy(from); + jid_destroy(sender); return (char *)plaintext; } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 7f9b7992..3cf8ea52 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -40,4 +40,4 @@ void omemo_start_device_session(const char *const jid, uint32_t device_id, GList gboolean omemo_loaded(void); gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt); -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); +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); diff --git a/src/ui/win_types.h b/src/ui/win_types.h index 6fe2811a..498d9bbe 100644 --- a/src/ui/win_types.h +++ b/src/ui/win_types.h @@ -168,6 +168,7 @@ typedef struct prof_muc_win_t { gboolean unread_mentions; gboolean unread_triggers; gboolean showjid; + gboolean is_omemo; unsigned long memcheck; char *enctext; char *message_char; diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 53190998..8fbaedd9 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -723,10 +723,16 @@ _handle_groupchat(xmpp_stanza_t *const stanza) return; } - message = xmpp_message_get_body(stanza); +#ifdef HAVE_OMEMO + message = omemo_receive_message(stanza); +#endif + if (!message) { - jid_destroy(jid); - return; + message = xmpp_message_get_body(stanza); + if (!message) { + jid_destroy(jid); + return; + } } // determine if the notifications happened whilst offline diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 0c033921..16610cb5 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -197,6 +197,8 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons char * omemo_receive_message(xmpp_stanza_t *const stanza) { + const char *type = xmpp_stanza_get_type(stanza); + xmpp_stanza_t *encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO); if (!encrypted) { return NULL; @@ -264,11 +266,10 @@ skip: } const char *from = xmpp_stanza_get_from(stanza); - Jid *jid = jid_create(from); - - char *plaintext = omemo_on_message_recv(jid->barejid, sid, iv_raw, iv_len, keys, payload_raw, payload_len); - jid_destroy(jid); + char *plaintext = omemo_on_message_recv(from, sid, iv_raw, iv_len, + keys, payload_raw, payload_len, + g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0); return plaintext; } -- cgit 1.4.1-2-gfad0 From 21f247d0eb67b90e0e012575c758b0f663d3b292 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 15 Mar 2019 07:28:21 +0140 Subject: Handle window type MUC in OMEMO start command --- src/command/cmd_funcs.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 01690867..f094be48 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7952,30 +7952,32 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) omemo_start_session(barejid); chatwin->is_omemo = TRUE; } else { - if (window->type != WIN_CHAT) { - win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session."); - return TRUE; - } + if (window->type == WIN_CHAT) { + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + if (chatwin->pgp_send) { + win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session."); + return TRUE; + } - ProfChatWin *chatwin = (ProfChatWin*)window; - assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); - if (chatwin->pgp_send) { - win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session."); - return TRUE; - } + if (chatwin->is_otr) { + win_println(window, THEME_DEFAULT, '!', "You must disable OTR encryption before starting an OMEMO session."); + return TRUE; + } - if (chatwin->is_otr) { - win_println(window, THEME_DEFAULT, '!', "You must disable OTR encryption before starting an OMEMO session."); - return TRUE; - } + if (chatwin->is_omemo) { + win_println(window, THEME_DEFAULT, '!', "You are already in an OMEMO session."); + return TRUE; + } - if (chatwin->is_omemo) { - win_println(window, THEME_DEFAULT, '!', "You are already in an OMEMO session."); + omemo_start_session(chatwin->barejid); + chatwin->is_omemo = TRUE; + } else if (window->type == WIN_MUC) { + } else { + win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session."); return TRUE; } - omemo_start_session(chatwin->barejid); - chatwin->is_omemo = TRUE; } return TRUE; -- cgit 1.4.1-2-gfad0 From cf528383bd78306f41b4645eff0d286a017d2d71 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 15 Mar 2019 19:32:28 +0140 Subject: Allow to start OMEMO in MUC --- src/command/cmd_funcs.c | 8 ++++++++ src/omemo/omemo.c | 13 +++++++++++++ src/omemo/omemo.h | 1 + 3 files changed, 22 insertions(+) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index f094be48..a882fa6d 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7973,6 +7973,14 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) omemo_start_session(chatwin->barejid); chatwin->is_omemo = TRUE; } else if (window->type == WIN_MUC) { + ProfMucWin *mucwin = (ProfMucWin*)window; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + + /* TODO: Check room is configured correctly, no anonymous and access to + * full jid */ + omemo_start_muc_sessions(mucwin->barejid); + + mucwin->is_omemo = TRUE; } else { win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session."); return TRUE; diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 62478e5d..29899cc1 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -294,6 +294,19 @@ omemo_start_session(const char *const barejid) } } +void +omemo_start_muc_sessions(const char *const barejid) +{ + GList *roster = muc_roster(barejid); + GList *iter; + for (iter = roster; iter != NULL; iter = iter->next) { + Occupant *occupant = (Occupant *)iter->data; + Jid *jid = jid_create(occupant->jid); + omemo_start_session(jid->barejid); + jid_destroy(jid); + } +} + gboolean omemo_loaded(void) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 3cf8ea52..6686cee2 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -36,6 +36,7 @@ void omemo_trust(const char *const jid, const char *const fingerprint); void omemo_untrust(const char *const jid, const char *const fingerprint); void omemo_start_session(const char *const barejid); +void omemo_start_muc_sessions(const char *const barejid); 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); -- cgit 1.4.1-2-gfad0 From 031e906035f27da3021f1dfb5acb8c9587b853ac Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 15 Mar 2019 22:58:01 +0100 Subject: Cosmetics rename variable barejid into roomjid --- src/command/cmd_funcs.c | 2 +- src/omemo/omemo.c | 4 ++-- src/omemo/omemo.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index a882fa6d..f21da6e7 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7978,7 +7978,7 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) /* TODO: Check room is configured correctly, no anonymous and access to * full jid */ - omemo_start_muc_sessions(mucwin->barejid); + omemo_start_muc_sessions(mucwin->roomjid); mucwin->is_omemo = TRUE; } else { diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 29899cc1..f7a305ee 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -295,9 +295,9 @@ omemo_start_session(const char *const barejid) } void -omemo_start_muc_sessions(const char *const barejid) +omemo_start_muc_sessions(const char *const roomjid) { - GList *roster = muc_roster(barejid); + GList *roster = muc_roster(roomjid); GList *iter; for (iter = roster; iter != NULL; iter = iter->next) { Occupant *occupant = (Occupant *)iter->data; diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 6686cee2..b56b50a5 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -36,7 +36,7 @@ void omemo_trust(const char *const jid, const char *const fingerprint); void omemo_untrust(const char *const jid, const char *const fingerprint); void omemo_start_session(const char *const barejid); -void omemo_start_muc_sessions(const char *const barejid); +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); -- cgit 1.4.1-2-gfad0 From f7ce1607f96f527b5c22e89510ab5185a200800d Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 15 Mar 2019 22:58:25 +0100 Subject: Ensure saved_account is set before calling sv_ev_login_account_success --- src/xmpp/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xmpp/session.c b/src/xmpp/session.c index 016fe76e..786b1cd5 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -308,11 +308,11 @@ session_login_success(gboolean secured) accounts_add(saved_details.name, saved_details.altdomain, saved_details.port, saved_details.tls_policy); accounts_set_jid(saved_details.name, saved_details.jid); - sv_ev_login_account_success(saved_details.name, secured); saved_account.name = strdup(saved_details.name); saved_account.passwd = strdup(saved_details.passwd); _session_free_saved_details(); + sv_ev_login_account_success(saved_account.name, secured); } roster_request(); -- cgit 1.4.1-2-gfad0 From a9d55dec9275c472d7eea2ff79304eda1aefd3be Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 18 Mar 2019 07:11:19 +0140 Subject: Add support for sending encrypted message in MUC --- src/event/client_events.c | 23 +++++++-- src/omemo/omemo.c | 127 +++++++++++++++++++++++++++++++--------------- src/omemo/omemo.h | 2 +- src/xmpp/message.c | 14 +++-- src/xmpp/xmpp.h | 2 +- 5 files changed, 118 insertions(+), 50 deletions(-) diff --git a/src/event/client_events.c b/src/event/client_events.c index 79ed88f6..1488174a 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -218,7 +218,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else { char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); chat_log_msg_out(chatwin->barejid, plugin_msg); @@ -238,7 +238,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else { gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); if (!handled) { @@ -261,7 +261,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -286,7 +286,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send(chatwin, plugin_msg, request_receipt); + omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); } else if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -334,10 +334,25 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const return; } +#ifdef HAVE_OMEMO + if (mucwin->is_omemo) { + omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE); + } else { + message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + } + + plugins_post_room_message_send(mucwin->roomjid, plugin_msg); + free(plugin_msg); + return; +#endif + +#ifndef HAVE_OMEMO message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); plugins_post_room_message_send(mucwin->roomjid, plugin_msg); free(plugin_msg); + return; +#endif } void diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index f7a305ee..3fb52b08 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -168,6 +169,7 @@ omemo_init(void) void omemo_on_connect(ProfAccount *account) { + GError *error = NULL; char *omemodir = files_get_data_path(DIR_OMEMO); GString *basedir = g_string_new(omemodir); free(omemodir); @@ -194,18 +196,26 @@ omemo_on_connect(ProfAccount *account) } } + omemo_devicelist_subscribe(); + omemo_ctx.identity_keyfile = g_key_file_new(); - if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + omemo_ctx.sessions_keyfile = g_key_file_new(); + + if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { load_identity(); omemo_generate_short_term_crypto_materials(account); + } else if (error->code != G_FILE_ERROR_NOENT) { + log_warning("OMEMO: error loading identity from: %s, %s", omemo_ctx.identity_filename->str, error->message); + return; } - omemo_ctx.sessions_keyfile = g_key_file_new(); - if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + error = NULL; + if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { load_sessions(); + } else if (error->code != G_FILE_ERROR_NOENT) { + log_warning("OMEMO: error loading sessions from: %s, %s", omemo_ctx.sessions_filename->str, error->message); } - omemo_devicelist_subscribe(); } void @@ -533,22 +543,15 @@ out: } gboolean -omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt) +omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc) { int res; gboolean ret = FALSE; Jid *jid = jid_create(connection_get_fulljid()); GList *keys = NULL; - GList *recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, chatwin->barejid); - if (!recipient_device_id) { - goto out; - } - GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid); - /* TODO generate fresh AES-GCM materials */ - /* TODO encrypt message */ unsigned char *key; unsigned char *iv; unsigned char *ciphertext; @@ -574,37 +577,72 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean memcpy(key_tag, key, AES128_GCM_KEY_LENGTH); memcpy(key_tag + AES128_GCM_KEY_LENGTH, tag, AES128_GCM_TAG_LENGTH); + GList *recipients = NULL; + if (muc) { + ProfMucWin *mucwin = (ProfMucWin *)win; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + GList *roster = muc_roster(mucwin->roomjid); + GList *iter; + for (iter = roster; iter != NULL; iter = iter->next) { + Occupant *occupant = (Occupant *)iter->data; + Jid *jid = jid_create(occupant->jid); + if (!jid->barejid) { + log_warning("OMEMO: missing barejid for MUC %s occupant %s", mucwin->roomjid, occupant->nick); + } else { + recipients = g_list_append(recipients, strdup(jid->barejid)); + } + jid_destroy(jid); + } + } else { + ProfChatWin *chatwin = (ProfChatWin *)win; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + recipients = g_list_append(recipients, strdup(chatwin->barejid)); + } + GList *device_ids_iter; - for (device_ids_iter = recipient_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { - int res; - ciphertext_message *ciphertext; - session_cipher *cipher; - signal_protocol_address address = { - .name = chatwin->barejid, - .name_len = strlen(chatwin->barejid), - .device_id = GPOINTER_TO_INT(device_ids_iter->data) - }; - res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); - if (res != 0) { - log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); + GList *recipients_iter; + for (recipients_iter = recipients; recipients_iter != NULL; recipients_iter = recipients_iter->next) { + GList *recipient_device_id = NULL; + recipient_device_id = g_hash_table_lookup(omemo_ctx.device_list, recipients_iter->data); + if (!recipient_device_id) { + log_warning("OMEMO: cannot find device ids for %s", recipients_iter->data); continue; } - res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); - if (res != 0) { - log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); - continue; + for (device_ids_iter = recipient_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { + int res; + ciphertext_message *ciphertext; + session_cipher *cipher; + signal_protocol_address address = { + .name = recipients_iter->data, + .name_len = strlen(recipients_iter->data), + .device_id = GPOINTER_TO_INT(device_ids_iter->data) + }; + + res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); + if (res != 0) { + log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); + continue; + } + + res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); + if (res != 0) { + log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); + continue; + } + signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + key->data = signal_buffer_data(buffer); + key->length = signal_buffer_len(buffer); + key->device_id = GPOINTER_TO_INT(device_ids_iter->data); + key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; + keys = g_list_append(keys, key); } - signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); - omemo_key_t *key = malloc(sizeof(omemo_key_t)); - key->data = signal_buffer_data(buffer); - key->length = signal_buffer_len(buffer); - key->device_id = GPOINTER_TO_INT(device_ids_iter->data); - key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; - keys = g_list_append(keys, key); } + g_list_free_full(recipients, free); + for (device_ids_iter = sender_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { int res; ciphertext_message *ciphertext; @@ -635,14 +673,23 @@ omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean keys = g_list_append(keys, key); } - char *id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt); - chat_log_omemo_msg_out(chatwin->barejid, message); - chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OMEMO, request_receipt); + if (muc) { + ProfMucWin *mucwin = (ProfMucWin *)win; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + char *id = message_send_chat_omemo(mucwin->roomjid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, TRUE); + free(id); + } else { + ProfChatWin *chatwin = (ProfChatWin *)win; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + char *id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, FALSE); + chat_log_omemo_msg_out(chatwin->barejid, message); + chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OMEMO, request_receipt); + free(id); + } ret = TRUE; out: jid_destroy(jid); - free(id); g_list_free_full(keys, free); free(ciphertext); gcry_free(key); @@ -659,7 +706,7 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, const unsigned char *const payload, size_t payload_len, gboolean muc) { unsigned char *plaintext = NULL; - Jid *sender; + Jid *sender = NULL; Jid *from = jid_create(from_jid); if (!from) { log_error("Invalid jid %s", from_jid); diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index b56b50a5..a8af1a66 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -40,5 +40,5 @@ 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); -gboolean omemo_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean request_receipt); +gboolean omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc); 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); diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 8fbaedd9..47a21438 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -318,13 +318,19 @@ char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, - gboolean request_receipt) + gboolean request_receipt, gboolean muc) { char *state = chat_session_get_state(jid); xmpp_ctx_t * const ctx = connection_get_ctx(); - char *id = connection_create_stanza_id("msg"); - - xmpp_stanza_t *message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id); + char *id; + xmpp_stanza_t *message; + if (muc) { + id = connection_create_stanza_id("muc"); + message = xmpp_message_new(ctx, STANZA_TYPE_GROUPCHAT, jid, id); + } else { + id = connection_create_stanza_id("msg"); + message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id); + } xmpp_stanza_t *encrypted = xmpp_stanza_new(ctx); xmpp_stanza_set_name(encrypted, "encrypted"); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index f2eec6c7..6675369a 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -140,7 +140,7 @@ char* message_send_chat(const char *const barejid, const char *const msg, const gboolean request_receipt); char* message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt); char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt); -char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt); +char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt, gboolean muc); void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url); void message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url); void message_send_groupchat_subject(const char *const roomjid, const char *const subject); -- cgit 1.4.1-2-gfad0 From 4e1ffa6bdb89269fa5eff9e5ee2484106d52d149 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 18 Mar 2019 20:30:28 +0140 Subject: Log and print outgoing encrypted message --- src/event/client_events.c | 29 +++++++++++++++---- src/event/server_events.c | 66 +++++++++++++++++++++++++++++++++++-------- src/event/server_events.h | 4 +-- src/log.c | 64 ++++++++++++++++++++++++++++++++++++++++- src/log.h | 7 +++-- src/omemo/omemo.c | 72 ++++++++++++++++++++++------------------------- src/omemo/omemo.h | 2 +- src/ui/mucwin.c | 46 ++++++++++++++++++++++-------- src/ui/ui.h | 3 +- src/ui/window.c | 1 + src/xmpp/message.c | 7 +++-- 11 files changed, 227 insertions(+), 74 deletions(-) diff --git a/src/event/client_events.c b/src/event/client_events.c index 1488174a..d1cdc797 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -218,7 +218,10 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + chat_log_omemo_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt); + free(id); } else { char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt); chat_log_msg_out(chatwin->barejid, plugin_msg); @@ -238,7 +241,10 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifndef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + chat_log_omemo_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt); + free(id); } else { gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt); if (!handled) { @@ -261,7 +267,10 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + chat_log_omemo_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt); + free(id); } else if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -286,7 +295,10 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo #ifdef HAVE_LIBGPGME #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE); + chat_log_omemo_msg_out(chatwin->barejid, plugin_msg); + chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt); + free(id); } else if (chatwin->pgp_send) { char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt); chat_log_pgp_msg_out(chatwin->barejid, plugin_msg); @@ -336,9 +348,14 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const #ifdef HAVE_OMEMO if (mucwin->is_omemo) { - omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE); + char *id = omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE); + groupchat_log_omemo_msg_out(mucwin->roomjid, plugin_msg); + mucwin_outgoing_msg(mucwin, plugin_msg, PROF_MSG_OMEMO); + free(id); } else { message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + groupchat_log_msg_out(mucwin->roomjid, plugin_msg); + mucwin_outgoing_msg(mucwin, plugin_msg, PROF_MSG_PLAIN); } plugins_post_room_message_send(mucwin->roomjid, plugin_msg); @@ -348,6 +365,8 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const #ifndef HAVE_OMEMO message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + groupchat_log_msg_out(mucwin->roomjid, plugin_msg); + mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); plugins_post_room_message_send(mucwin->roomjid, plugin_msg); free(plugin_msg); diff --git a/src/event/server_events.c b/src/event/server_events.c index 7f293198..fd719cc1 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -260,21 +260,26 @@ sv_ev_room_history(const char *const room_jid, const char *const nick, } void -sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message) +sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message, gboolean omemo) { - if (prefs_get_boolean(PREF_GRLOG)) { - Jid *jid = jid_create(connection_get_fulljid()); - groupchat_log_chat(jid->barejid, room_jid, nick, message); - jid_destroy(jid); - } - ProfMucWin *mucwin = wins_get_muc(room_jid); if (!mucwin) { return; } - char *new_message = plugins_pre_room_message_display(room_jid, nick, message); char *mynick = muc_nick(mucwin->roomjid); + if (g_strcmp0(mynick, nick) == 0) { + /* Ignore message reflection */ + return; + } + + if (omemo) { + groupchat_log_omemo_msg_in(room_jid, nick, message); + } else { + groupchat_log_msg_in(room_jid, nick, message); + } + + char *new_message = plugins_pre_room_message_display(room_jid, nick, message); gboolean whole_word = prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD); gboolean case_sensitive = prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE); @@ -289,7 +294,11 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha GList *triggers = prefs_message_get_triggers(new_message); - mucwin_message(mucwin, nick, new_message, mentions, triggers); + if (omemo) { + mucwin_incoming_msg(mucwin, nick, new_message, mentions, triggers, PROF_MSG_OMEMO); + } else { + mucwin_incoming_msg(mucwin, nick, new_message, mentions, triggers, PROF_MSG_PLAIN); + } g_slist_free(mentions); @@ -607,15 +616,50 @@ sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_me } #ifdef HAVE_LIBGPGME +#ifndef HAVE_OMEMO if (pgp_message) { _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, NULL); } else { _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL); } -#else - _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL); + rosterwin_roster(); + return; +#endif #endif + +#ifdef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (pgp_message) { + _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, NULL); + } else if (omemo) { + _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, NULL); + } else { + _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL); + } rosterwin_roster(); + return; +#endif +#endif + +#ifndef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (omemo) { + _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, NULL); + } else { + _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL); + } + rosterwin_roster(); + return; +#endif +#endif + +#ifndef HAVE_LIBGPGME +#ifndef HAVE_OMEMO + _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL); + rosterwin_roster(); + return; +#endif +#endif } void diff --git a/src/event/server_events.h b/src/event/server_events.h index 96fdb58c..0fa9989f 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -46,9 +46,9 @@ void sv_ev_room_invite(jabber_invite_t invite_type, void sv_ev_room_broadcast(const char *const room_jid, const char *const message); void sv_ev_room_subject(const char *const room, const char *const nick, const char *const subject); void sv_ev_room_history(const char *const room_jid, const char *const nick, - GDateTime *timestamp, const char *const message); + GDateTime *timestamp, const char *const message, gboolean omemo); void sv_ev_room_message(const char *const room_jid, const char *const nick, - const char *const message); + const char *const message, gboolean omemo); void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo); void sv_ev_incoming_private_message(const char *const fulljid, char *message); void sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTime *timestamp); diff --git a/src/log.c b/src/log.c index 090fd2f8..b679eb1a 100644 --- a/src/log.c +++ b/src/log.c @@ -48,6 +48,7 @@ #include "config/files.h" #include "config/preferences.h" #include "xmpp/xmpp.h" +#include "xmpp/muc.h" #define PROF "prof" @@ -89,6 +90,8 @@ static void _rotate_log_file(void); static char* _log_string_from_level(log_level_t level); static void _chat_log_chat(const char *const login, const char *const other, const gchar *const msg, chat_log_direction_t direction, GDateTime *timestamp); +static void _groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick, + const gchar *const msg); void log_debug(const char *const msg, ...) @@ -440,7 +443,66 @@ _chat_log_chat(const char *const login, const char *const other, const char *con } void -groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick, const gchar *const msg) +groupchat_log_msg_out(const gchar *const room, const gchar *const msg) +{ + if (prefs_get_boolean(PREF_GRLOG)) { + const char *jid = connection_get_fulljid(); + Jid *jidp = jid_create(jid); + char *mynick = muc_nick(room); + _groupchat_log_chat(jidp->barejid, room, mynick, msg); + jid_destroy(jidp); + } +} + +void +groupchat_log_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg) +{ + if (prefs_get_boolean(PREF_GRLOG)) { + const char *jid = connection_get_fulljid(); + Jid *jidp = jid_create(jid); + _groupchat_log_chat(jidp->barejid, room, nick, msg); + jid_destroy(jidp); + } +} + +void +groupchat_log_omemo_msg_out(const gchar *const room, const gchar *const msg) +{ + if (prefs_get_boolean(PREF_CHLOG)) { + const char *jid = connection_get_fulljid(); + Jid *jidp = jid_create(jid); + char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG); + char *mynick = muc_nick(room); + if (strcmp(pref_omemo_log, "on") == 0) { + _groupchat_log_chat(jidp->barejid, room, mynick, msg); + } else if (strcmp(pref_omemo_log, "redact") == 0) { + _groupchat_log_chat(jidp->barejid, room, mynick, "[redacted]"); + } + prefs_free_string(pref_omemo_log); + jid_destroy(jidp); + } +} + +void +groupchat_log_omemo_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg) +{ + if (prefs_get_boolean(PREF_CHLOG)) { + const char *jid = connection_get_fulljid(); + Jid *jidp = jid_create(jid); + char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG); + if (strcmp(pref_omemo_log, "on") == 0) { + _groupchat_log_chat(jidp->barejid, room, nick, msg); + } else if (strcmp(pref_omemo_log, "redact") == 0) { + _groupchat_log_chat(jidp->barejid, room, nick, "[redacted]"); + } + prefs_free_string(pref_omemo_log); + jid_destroy(jidp); + } +} + +void +_groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick, + const gchar *const msg) { struct dated_chat_log *dated_log = g_hash_table_lookup(groupchat_logs, room); diff --git a/src/log.h b/src/log.h index b14231b7..1f45545c 100644 --- a/src/log.h +++ b/src/log.h @@ -82,7 +82,10 @@ void chat_log_close(void); GSList* chat_log_get_previous(const gchar *const login, const gchar *const recipient); void groupchat_log_init(void); -void groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick, - const gchar *const msg); + +void groupchat_log_msg_out(const gchar *const room, const gchar *const msg); +void groupchat_log_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg); +void groupchat_log_omemo_msg_out(const gchar *const room, const gchar *const msg); +void groupchat_log_omemo_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg); #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 3fb52b08..548c3b9c 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -542,16 +542,14 @@ out: jid_destroy(ownjid); } -gboolean +char * omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc) { + char *id = NULL; int res; - gboolean ret = FALSE; Jid *jid = jid_create(connection_get_fulljid()); GList *keys = NULL; - GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid); - unsigned char *key; unsigned char *iv; unsigned char *ciphertext; @@ -643,50 +641,48 @@ omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_ g_list_free_full(recipients, free); - for (device_ids_iter = sender_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { - int res; - ciphertext_message *ciphertext; - session_cipher *cipher; - signal_protocol_address address = { - .name = jid->barejid, - .name_len = strlen(jid->barejid), - .device_id = GPOINTER_TO_INT(device_ids_iter->data) - }; - - res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); - if (res != 0) { - log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); - continue; - } + if (!muc) { + GList *sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid); + for (device_ids_iter = sender_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) { + int res; + ciphertext_message *ciphertext; + session_cipher *cipher; + signal_protocol_address address = { + .name = jid->barejid, + .name_len = strlen(jid->barejid), + .device_id = GPOINTER_TO_INT(device_ids_iter->data) + }; - res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); - if (res != 0) { - log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); - continue; + res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal); + if (res != 0) { + log_error("OMEMO: cannot create cipher for %s device id %d", address.name, address.device_id); + continue; + } + + res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); + if (res != 0) { + log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); + continue; + } + signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); + omemo_key_t *key = malloc(sizeof(omemo_key_t)); + key->data = signal_buffer_data(buffer); + key->length = signal_buffer_len(buffer); + key->device_id = GPOINTER_TO_INT(device_ids_iter->data); + key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; + keys = g_list_append(keys, key); } - signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); - omemo_key_t *key = malloc(sizeof(omemo_key_t)); - key->data = signal_buffer_data(buffer); - key->length = signal_buffer_len(buffer); - key->device_id = GPOINTER_TO_INT(device_ids_iter->data); - key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; - keys = g_list_append(keys, key); } if (muc) { ProfMucWin *mucwin = (ProfMucWin *)win; assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); - char *id = message_send_chat_omemo(mucwin->roomjid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, TRUE); - free(id); + id = message_send_chat_omemo(mucwin->roomjid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, TRUE); } else { ProfChatWin *chatwin = (ProfChatWin *)win; assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); - char *id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, FALSE); - chat_log_omemo_msg_out(chatwin->barejid, message); - chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OMEMO, request_receipt); - free(id); + id = message_send_chat_omemo(chatwin->barejid, omemo_ctx.device_id, keys, iv, AES128_GCM_IV_LENGTH, ciphertext, ciphertext_len, request_receipt, FALSE); } - ret = TRUE; out: jid_destroy(jid); @@ -697,7 +693,7 @@ out: gcry_free(tag); gcry_free(key_tag); - return ret; + return id; } char * diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index a8af1a66..e78bd09b 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -40,5 +40,5 @@ 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); -gboolean omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc); +char * omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc); 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); diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c index 0f9f4f2b..abbcd21c 100644 --- a/src/ui/mucwin.c +++ b/src/ui/mucwin.c @@ -478,7 +478,7 @@ _mucwin_print_triggers(ProfWin *window, const char *const message, GList *trigge } void -mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers) +mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, prof_enc_t enc_mode) { assert(mucwin != NULL); @@ -488,19 +488,43 @@ mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const mes char ch = '-'; if (mucwin->message_char) { ch = mucwin->message_char[0]; + } else if (enc_mode == PROF_MSG_OTR) { + ch = prefs_get_otr_char(); + } else if (enc_mode == PROF_MSG_PGP) { + ch = prefs_get_pgp_char(); + } else if (enc_mode == PROF_MSG_OMEMO) { + ch = prefs_get_omemo_char(); } - if (g_strcmp0(nick, mynick) != 0) { - if (g_slist_length(mentions) > 0) { - _mucwin_print_mention(window, message, nick, mynick, mentions, &ch); - } else if (triggers) { - win_print_them(window, THEME_ROOMTRIGGER, ch, nick); - _mucwin_print_triggers(window, message, triggers); - } else { - win_println_them_message(window, ch, nick, "%s", message); - } + win_println_me_message(window, ch, mynick, "%s", message); +} + +void +mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers, prof_enc_t enc_mode) +{ + assert(mucwin != NULL); + + ProfWin *window = (ProfWin*)mucwin; + char *mynick = muc_nick(mucwin->roomjid); + + char ch = '-'; + if (mucwin->message_char) { + ch = mucwin->message_char[0]; + } else if (enc_mode == PROF_MSG_OTR) { + ch = prefs_get_otr_char(); + } else if (enc_mode == PROF_MSG_PGP) { + ch = prefs_get_pgp_char(); + } else if (enc_mode == PROF_MSG_OMEMO) { + ch = prefs_get_omemo_char(); + } + + if (g_slist_length(mentions) > 0) { + _mucwin_print_mention(window, message, nick, mynick, mentions, &ch); + } else if (triggers) { + win_print_them(window, THEME_ROOMTRIGGER, ch, nick); + _mucwin_print_triggers(window, message, triggers); } else { - win_println_me_message(window, ch, mynick, "%s", message); + win_println_them_message(window, ch, nick, "%s", message); } } diff --git a/src/ui/ui.h b/src/ui/ui.h index 95d291b4..e3cece81 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -162,7 +162,8 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char const char *const role, const char *const affiliation, const char *const actor, const char *const reason); void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char *const presence); void mucwin_history(ProfMucWin *mucwin, const char *const nick, GDateTime *timestamp, const char *const message); -void mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers); +void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, prof_enc_t enc_mode); +void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers, prof_enc_t enc_mode); void mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const subject); void mucwin_requires_config(ProfMucWin *mucwin); void mucwin_info(ProfMucWin *mucwin); diff --git a/src/ui/window.c b/src/ui/window.c index ef0f93d2..64b04365 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -197,6 +197,7 @@ win_create_muc(const char *const roomjid) } new_win->enctext = NULL; new_win->message_char = NULL; + new_win->is_omemo = FALSE; new_win->memcheck = PROFMUCWIN_MEMCHECK; diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 47a21438..cc3d66bd 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -729,8 +729,11 @@ _handle_groupchat(xmpp_stanza_t *const stanza) return; } + // check omemo encryption + gboolean omemo = FALSE; #ifdef HAVE_OMEMO message = omemo_receive_message(stanza); + omemo = message != NULL; #endif if (!message) { @@ -744,10 +747,10 @@ _handle_groupchat(xmpp_stanza_t *const stanza) // determine if the notifications happened whilst offline GDateTime *timestamp = stanza_get_delay(stanza); if (timestamp) { - sv_ev_room_history(jid->barejid, jid->resourcepart, timestamp, message); + sv_ev_room_history(jid->barejid, jid->resourcepart, timestamp, message, omemo); g_date_time_unref(timestamp); } else { - sv_ev_room_message(jid->barejid, jid->resourcepart, message); + sv_ev_room_message(jid->barejid, jid->resourcepart, message, omemo); } xmpp_free(ctx, message); -- cgit 1.4.1-2-gfad0 From 88670a7f0b1275d9a0b2eebc0cdf5ff0e1cdc337 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Mar 2019 12:46:12 +0100 Subject: Add OMEMO state in titlebar --- src/ui/titlebar.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c index f519fdd2..6c8fe3aa 100644 --- a/src/ui/titlebar.c +++ b/src/ui/titlebar.c @@ -421,6 +421,21 @@ _show_privacy(ProfChatWin *chatwin) return; } + if (chatwin->is_omemo) { + wprintw(win, " "); + wattron(win, bracket_attrs); + wprintw(win, "["); + wattroff(win, bracket_attrs); + wattron(win, encrypted_attrs); + wprintw(win, "OMEMO"); + wattroff(win, encrypted_attrs); + wattron(win, bracket_attrs); + wprintw(win, "]"); + wattroff(win, bracket_attrs); + + return; + } + if (prefs_get_boolean(PREF_ENC_WARN)) { wprintw(win, " "); wattron(win, bracket_attrs); -- cgit 1.4.1-2-gfad0 From e5b01ed71b415c51abcdb63bb4dc1482b0d9248d Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Mar 2019 12:55:56 +0100 Subject: Add OMEMO state in titlebar in MUC --- src/ui/titlebar.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c index 6c8fe3aa..e1758d81 100644 --- a/src/ui/titlebar.c +++ b/src/ui/titlebar.c @@ -321,6 +321,21 @@ _show_muc_privacy(ProfMucWin *mucwin) int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET); int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED); + if (mucwin->is_omemo) { + wprintw(win, " "); + wattron(win, bracket_attrs); + wprintw(win, "["); + wattroff(win, bracket_attrs); + wattron(win, encrypted_attrs); + wprintw(win, "OMEMO"); + wattroff(win, encrypted_attrs); + wattron(win, bracket_attrs); + wprintw(win, "]"); + wattroff(win, bracket_attrs); + + return; + } + if (mucwin->enctext) { wprintw(win, " "); wattron(win, bracket_attrs); -- cgit 1.4.1-2-gfad0 From 9044e3732c5f6f53e3aa6f0205bd9e05b6caace3 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Mar 2019 18:39:58 +0140 Subject: Ensure room isn't anonymous --- src/command/cmd_funcs.c | 4 ++++ src/xmpp/xmpp.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index f21da6e7..4c8accd0 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7978,6 +7978,10 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) /* TODO: Check room is configured correctly, no anonymous and access to * full jid */ + if (!caps_jid_has_feature(mucwin->roomjid, XMPP_FEATURE_MUC_NONANONYMOUS)) { + win_println(window, THEME_DEFAULT, '!', "MUC is anonymous, can't enable OMEMO."); + return TRUE; + } omemo_start_muc_sessions(mucwin->roomjid); mucwin->is_omemo = TRUE; diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 6675369a..763c8a91 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -60,6 +60,7 @@ #define XMPP_FEATURE_RECEIPTS "urn:xmpp:receipts" #define XMPP_FEATURE_LASTACTIVITY "jabber:iq:last" #define XMPP_FEATURE_MUC "http://jabber.org/protocol/muc" +#define XMPP_FEATURE_MUC_NONANONYMOUS "http://jabber.org/protocol/muc#muc_nonanonymous" #define XMPP_FEATURE_COMMANDS "http://jabber.org/protocol/commands" #define XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY "eu.siacs.conversations.axolotl.devicelist+notify" -- cgit 1.4.1-2-gfad0 From d8f0bcef9407f45161e124aa4703d0f0485fb2db Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Mar 2019 18:40:24 +0140 Subject: Add publish option to OMEMO device list and bundle --- src/xmpp/omemo.c | 14 ++++++++++++++ src/xmpp/stanza.c | 39 +++++++++++++++++++++++++++++++++++++++ src/xmpp/stanza.h | 2 ++ src/xmpp/xmpp.h | 2 ++ 4 files changed, 57 insertions(+) diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 16610cb5..1a10e374 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -22,6 +22,13 @@ omemo_devicelist_publish(GList *device_list) { xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, device_list); + + Jid *jid = jid_create(connection_get_fulljid()); + if (caps_jid_has_feature(jid->barejid, XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { + stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); + } + jid_destroy(jid); + iq_send_stanza(iq); xmpp_stanza_release(iq); } @@ -62,6 +69,13 @@ omemo_bundle_publish(void) identity_key, identity_key_length, signed_prekey, signed_prekey_length, signed_prekey_signature, signed_prekey_signature_length, prekeys, ids, lengths); + + Jid *jid = jid_create(connection_get_fulljid()); + if (caps_jid_has_feature(jid->barejid, XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { + stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); + } + jid_destroy(jid); + iq_send_stanza(iq); xmpp_stanza_release(iq); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 2b2ec0d8..65a6e02b 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -1832,6 +1832,45 @@ stanza_get_error_message(xmpp_stanza_t *stanza) return strdup("unknown"); } +void +stanza_attach_publish_options(xmpp_ctx_t *const ctx, xmpp_stanza_t *const iq, const char *const option, const char *const value) +{ + xmpp_stanza_t *publish_options = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(publish_options, STANZA_NAME_PUBLISH_OPTIONS); + + xmpp_stanza_t *x = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(x, STANZA_NAME_X); + xmpp_stanza_set_ns(x, STANZA_NS_DATA); + xmpp_stanza_set_type(x, "submit"); + xmpp_stanza_add_child(publish_options, x); + + xmpp_stanza_t *form_type = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(form_type, STANZA_NAME_FIELD); + xmpp_stanza_set_attribute(form_type, STANZA_ATTR_VAR, "FORM_TYPE"); + xmpp_stanza_set_type(form_type, "hidden"); + xmpp_stanza_t *form_type_value = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(form_type_value, STANZA_NAME_VALUE); + xmpp_stanza_t *form_type_value_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(form_type_value_text, XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS); + xmpp_stanza_add_child(form_type_value, form_type_value_text); + xmpp_stanza_add_child(form_type, form_type_value); + xmpp_stanza_add_child(x, form_type); + + xmpp_stanza_t *access_model = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(access_model, STANZA_NAME_FIELD); + xmpp_stanza_set_attribute(access_model, STANZA_ATTR_VAR, option); + xmpp_stanza_t *access_model_value = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(access_model_value, STANZA_NAME_VALUE); + xmpp_stanza_t *access_model_value_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(access_model_value_text, value); + xmpp_stanza_add_child(access_model_value, access_model_value_text); + xmpp_stanza_add_child(access_model, access_model_value); + xmpp_stanza_add_child(x, access_model); + + xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(iq, STANZA_NS_PUBSUB); + xmpp_stanza_add_child(pubsub, publish_options); +} + void stanza_attach_priority(xmpp_ctx_t *const ctx, xmpp_stanza_t *const presence, const int pri) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index f392eeff..2efb4cff 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -290,6 +290,8 @@ xmpp_stanza_t* stanza_create_room_kick_iq(xmpp_ctx_t *const ctx, const char *con xmpp_stanza_t* stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node); xmpp_stanza_t* stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, const char *const node, const char *const sessionid, DataForm *form); +void stanza_attach_publish_options(xmpp_ctx_t *const ctx, xmpp_stanza_t *const publish, const char *const option, const char *const value); + xmpp_stanza_t* stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 763c8a91..67cc722c 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -63,6 +63,8 @@ #define XMPP_FEATURE_MUC_NONANONYMOUS "http://jabber.org/protocol/muc#muc_nonanonymous" #define XMPP_FEATURE_COMMANDS "http://jabber.org/protocol/commands" #define XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY "eu.siacs.conversations.axolotl.devicelist+notify" +#define XMPP_FEATURE_PUBSUB "http://jabber.org/protocol/pubsub" +#define XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS "http://jabber.org/protocol/pubsub#publish-options" typedef enum { JABBER_CONNECTING, -- cgit 1.4.1-2-gfad0 From 70109953ec72e54337e787f5140f449646770f7e Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Mar 2019 16:50:06 +0100 Subject: Revert "Ensure room isn't anonymous" This reverts commit 733e1a24c7e08dde6aa2c15ec4528220aa360845. --- src/command/cmd_funcs.c | 4 ---- src/xmpp/xmpp.h | 1 - 2 files changed, 5 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 4c8accd0..f21da6e7 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7978,10 +7978,6 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) /* TODO: Check room is configured correctly, no anonymous and access to * full jid */ - if (!caps_jid_has_feature(mucwin->roomjid, XMPP_FEATURE_MUC_NONANONYMOUS)) { - win_println(window, THEME_DEFAULT, '!', "MUC is anonymous, can't enable OMEMO."); - return TRUE; - } omemo_start_muc_sessions(mucwin->roomjid); mucwin->is_omemo = TRUE; diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 67cc722c..39877bc2 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -60,7 +60,6 @@ #define XMPP_FEATURE_RECEIPTS "urn:xmpp:receipts" #define XMPP_FEATURE_LASTACTIVITY "jabber:iq:last" #define XMPP_FEATURE_MUC "http://jabber.org/protocol/muc" -#define XMPP_FEATURE_MUC_NONANONYMOUS "http://jabber.org/protocol/muc#muc_nonanonymous" #define XMPP_FEATURE_COMMANDS "http://jabber.org/protocol/commands" #define XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY "eu.siacs.conversations.axolotl.devicelist+notify" #define XMPP_FEATURE_PUBSUB "http://jabber.org/protocol/pubsub" -- cgit 1.4.1-2-gfad0 From 9a66e74986876ded37bb027bd0f673cd1f975782 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Mar 2019 17:10:06 +0100 Subject: Handle absent muc member --- src/omemo/omemo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 548c3b9c..6e723891 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -734,6 +734,10 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, break; } } + if (!sender) { + log_warning("OMEMO: cannot find MUC message sender fulljid"); + goto out; + } } else { sender = jid_create(from->barejid); } -- cgit 1.4.1-2-gfad0 From bc541cb8ac6be40a2ee1e38a99de7cf144361a2a Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 19 Mar 2019 17:10:20 +0100 Subject: Add OMEMO end command --- src/command/cmd_ac.c | 1 + src/command/cmd_defs.c | 3 +++ src/command/cmd_funcs.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/command/cmd_funcs.h | 1 + 4 files changed, 46 insertions(+) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 588c8b4f..8db9c035 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -579,6 +579,7 @@ cmd_ac_init(void) omemo_ac = autocomplete_new(); autocomplete_add(omemo_ac, "gen"); autocomplete_add(omemo_ac, "start"); + autocomplete_add(omemo_ac, "end"); autocomplete_add(omemo_ac, "trust"); autocomplete_add(omemo_ac, "untrust"); autocomplete_add(omemo_ac, "fingerprint"); diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 70e4595e..2d4ae061 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2335,6 +2335,7 @@ static struct cmd_t command_defs[] = CMD_SUBFUNCS( { "gen", cmd_omemo_gen }, { "start", cmd_omemo_start }, + { "end", cmd_omemo_end }, { "trust", cmd_omemo_trust }, { "untrust", cmd_omemo_untrust }, { "fingerprint", cmd_omemo_fingerprint }) @@ -2346,12 +2347,14 @@ static struct cmd_t command_defs[] = "/omemo gen", "/omemo start []", "/omemo trust [] ", + "/omemo end", "/omemo fingerprint") CMD_DESC( "Omemo commands to manage keys, and perform encryption during chat sessions.") CMD_ARGS( { "gen", "Generate OMEMO crytographic materials for current account." }, { "start []", "Start an OMEMO session with contact, or current recipient if omitted." }, + { "end", "End the current OMEMO session," }, { "fingerprint", "Show current device fingerprint." }) CMD_EXAMPLES( "/omemo gen", diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index f21da6e7..49e20744 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7995,6 +7995,47 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) #endif } +gboolean +cmd_omemo_end(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OMEMO information."); + return TRUE; + } + + if (window->type == WIN_CHAT) { + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + + if (!chatwin->is_omemo) { + win_println(window, THEME_DEFAULT, '!', "You are not currently in an OMEMO session."); + return TRUE; + } + + chatwin->is_omemo = FALSE; + } else if (window->type == WIN_MUC) { + ProfMucWin *mucwin = (ProfMucWin*)window; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + + if (!mucwin->is_omemo) { + win_println(window, THEME_DEFAULT, '!', "You are not currently in an OMEMO session."); + return TRUE; + } + + mucwin->is_omemo = FALSE; + } else { + win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session."); + return TRUE; + } + + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} + gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args) { diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index efa8cd83..21e7a51f 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -216,6 +216,7 @@ gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args); -- cgit 1.4.1-2-gfad0 From 91ee289c773a137c60e98c1e62db41346eb57363 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Mar 2019 00:03:16 +0100 Subject: Add support for disconnect in OMEMO --- src/event/client_events.c | 3 ++ src/omemo/omemo.c | 98 ++++++++++++++++++++++++++++++++++++----------- src/omemo/omemo.h | 4 +- src/omemo/store.c | 16 ++++++-- src/xmpp/message.c | 1 + src/xmpp/omemo.c | 42 +++++++++++++++----- src/xmpp/stanza.c | 16 ++++++-- 7 files changed, 140 insertions(+), 40 deletions(-) diff --git a/src/event/client_events.c b/src/event/client_events.c index d1cdc797..adcffbf2 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -98,6 +98,9 @@ cl_ev_disconnect(void) #ifdef HAVE_LIBGPGME p_gpg_on_disconnect(); #endif +#ifdef HAVE_OMEMO + omemo_on_disconnect(); +#endif } void diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 6e723891..8ec0554f 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -35,7 +35,6 @@ static void unlock(void *user_data); static void omemo_log(int level, const char *message, size_t len, void *user_data); static gboolean handle_own_device_list(const char *const jid, GList *device_list); static gboolean handle_device_list_start_session(const char *const jid, GList *device_list); -static void free_omemo_key(omemo_key_t *key); static char * omemo_fingerprint(ec_public_key *identity, gboolean formatted); static unsigned char *omemo_fingerprint_decode(const char *const fingerprint, size_t *len); static void cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity); @@ -196,6 +195,8 @@ omemo_on_connect(ProfAccount *account) } } + g_string_free(basedir, TRUE); + omemo_devicelist_subscribe(); omemo_ctx.identity_keyfile = g_key_file_new(); @@ -218,6 +219,28 @@ omemo_on_connect(ProfAccount *account) } +void +omemo_on_disconnect(void) +{ + signal_protocol_signed_pre_key_remove_key(omemo_ctx.store, omemo_ctx.signed_pre_key_id); + g_hash_table_free(omemo_ctx.signed_pre_key_store); + + GHashTableIter iter; + gpointer id; + + g_hash_table_iter_init(&iter, omemo_ctx.pre_key_store); + while (g_hash_table_iter_next(&iter, &id, NULL)) { + signal_protocol_pre_key_remove_key(omemo_ctx.store, GPOINTER_TO_INT(id)); + } + + g_hash_table_free(omemo_ctx.pre_key_store); + + g_string_free(omemo_ctx.identity_filename, TRUE); + g_key_file_free(omemo_ctx.identity_keyfile); + g_string_free(omemo_ctx.sessions_filename, TRUE); + g_key_file_free(omemo_ctx.sessions_keyfile); +} + void omemo_generate_crypto_materials(ProfAccount *account) { @@ -261,6 +284,13 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) signal_protocol_key_helper_pre_key_list_node *pre_keys_head; signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start, 100, omemo_ctx.signal); + signal_protocol_key_helper_pre_key_list_node *p; + for (p = pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { + session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); + signal_protocol_pre_key_store_key(omemo_ctx.store, prekey); + } + signal_protocol_key_helper_key_list_free(pre_keys_head); + session_signed_pre_key *signed_pre_key; struct timeval tv; gettimeofday(&tv, NULL); @@ -268,13 +298,8 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) omemo_ctx.signed_pre_key_id = 1; signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, omemo_ctx.identity_key_pair, omemo_ctx.signed_pre_key_id, timestamp, omemo_ctx.signal); - - signal_protocol_key_helper_pre_key_list_node *p; - for (p = pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { - session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); - signal_protocol_pre_key_store_key(omemo_ctx.store, prekey); - } signal_protocol_signed_pre_key_store_key(omemo_ctx.store, signed_pre_key); + SIGNAL_UNREF(signed_pre_key); loaded = TRUE; @@ -315,6 +340,7 @@ omemo_start_muc_sessions(const char *const roomjid) omemo_start_session(jid->barejid); jid_destroy(jid); } + g_list_free(roster); } gboolean @@ -348,6 +374,7 @@ omemo_signed_prekey(unsigned char **output, size_t *length) signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id); ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(signed_pre_key))); + SIGNAL_UNREF(signed_pre_key); *length = signal_buffer_len(buffer); *output = malloc(*length); memcpy(*output, signal_buffer_data(buffer), *length); @@ -363,6 +390,7 @@ omemo_signed_prekey_signature(unsigned char **output, size_t *length) *length = session_signed_pre_key_get_signature_len(signed_pre_key); *output = malloc(*length); memcpy(*output, session_signed_pre_key_get_signature(signed_pre_key), *length); + SIGNAL_UNREF(signed_pre_key); } void @@ -382,9 +410,12 @@ omemo_prekeys(GList **prekeys, GList **ids, GList **lengths) signal_buffer *public_key; ec_public_key_serialize(&public_key, ec_key_pair_get_public(session_pre_key_get_key_pair(pre_key))); + SIGNAL_UNREF(pre_key); size_t length = signal_buffer_len(public_key); unsigned char *prekey_value = malloc(length); memcpy(prekey_value, signal_buffer_data(public_key), length); + signal_buffer_free(public_key); + *prekeys = g_list_append(*prekeys, prekey_value); *ids = g_list_append(*ids, GINT_TO_POINTER(id)); *lengths = g_list_append(*lengths, GINT_TO_POINTER(length)); @@ -454,7 +485,7 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const identity_key_raw, size_t identity_key_len) { signal_protocol_address address = { - .name = strdup(jid), + .name = jid, .name_len = strlen(jid), .device_id = device_id, }; @@ -538,7 +569,7 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, } out: - g_list_free_full(prekeys, (GDestroyNotify)free_omemo_key); + SIGNAL_UNREF(identity_key); jid_destroy(ownjid); } @@ -591,6 +622,7 @@ omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_ } jid_destroy(jid); } + g_list_free(roster); } else { ProfChatWin *chatwin = (ProfChatWin *)win; assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); @@ -625,17 +657,20 @@ omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_ } res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); + session_cipher_free(cipher); if (res != 0) { log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); continue; } signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); omemo_key_t *key = malloc(sizeof(omemo_key_t)); - key->data = signal_buffer_data(buffer); key->length = signal_buffer_len(buffer); + key->data = malloc(key->length); + memcpy(key->data, signal_buffer_data(buffer), key->length); key->device_id = GPOINTER_TO_INT(device_ids_iter->data); key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; keys = g_list_append(keys, key); + SIGNAL_UNREF(ciphertext); } } @@ -660,17 +695,20 @@ omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_ } res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext); + session_cipher_free(cipher); if (res != 0) { log_error("OMEMO: cannot encrypt key for %s device id %d", address.name, address.device_id); continue; } signal_buffer *buffer = ciphertext_message_get_serialized(ciphertext); omemo_key_t *key = malloc(sizeof(omemo_key_t)); - key->data = signal_buffer_data(buffer); key->length = signal_buffer_len(buffer); + key->data = malloc(key->length); + memcpy(key->data, signal_buffer_data(buffer), key->length); key->device_id = GPOINTER_TO_INT(device_ids_iter->data); key->prekey = ciphertext_message_get_type(ciphertext) == CIPHERTEXT_PREKEY_TYPE; keys = g_list_append(keys, key); + SIGNAL_UNREF(ciphertext); } } @@ -686,7 +724,7 @@ omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_ out: jid_destroy(jid); - g_list_free_full(keys, free); + g_list_free_full(keys, (GDestroyNotify)omemo_key_free); free(ciphertext); gcry_free(key); gcry_free(iv); @@ -734,6 +772,7 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, break; } } + g_list_free(roster); if (!sender) { log_warning("OMEMO: cannot find MUC message sender fulljid"); goto out; @@ -770,6 +809,9 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, curve_generate_key_pair(omemo_ctx.signal, &ec_pair); session_pre_key_create(&new_pre_key, pre_key_id, ec_pair); signal_protocol_pre_key_store_key(omemo_ctx.store, new_pre_key); + SIGNAL_UNREF(new_pre_key); + SIGNAL_UNREF(message); + SIGNAL_UNREF(ec_pair); omemo_bundle_publish(); if (res == 0) { @@ -781,16 +823,19 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, signal_message *message; signal_message_deserialize(&message, key->data, key->length, omemo_ctx.signal); res = session_cipher_decrypt_signal_message(cipher, message, NULL, &plaintext_key); + SIGNAL_UNREF(message); } + + session_cipher_free(cipher); if (res != 0) { log_error("OMEMO: cannot decrypt message key"); - return NULL; + goto out; } if (signal_buffer_len(plaintext_key) != AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH) { log_error("OMEMO: invalid key length"); signal_buffer_free(plaintext_key); - return NULL; + goto out; } size_t plaintext_len = payload_len; @@ -798,11 +843,12 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, res = aes128gcm_decrypt(plaintext, &plaintext_len, payload, payload_len, iv, signal_buffer_data(plaintext_key), signal_buffer_data(plaintext_key) + AES128_GCM_KEY_LENGTH); + signal_buffer_free(plaintext_key); if (res != 0) { log_error("OMEMO: cannot decrypt message: %s", gcry_strerror(res)); - signal_buffer_free(plaintext_key); free(plaintext); - return NULL; + plaintext = NULL; + goto out; } plaintext[plaintext_len] = '\0'; @@ -1040,14 +1086,14 @@ handle_device_list_start_session(const char *const jid, GList *device_list) return FALSE; } -static void -free_omemo_key(omemo_key_t *key) +void +omemo_key_free(omemo_key_t *key) { if (key == NULL) { return; } - free((void *)key->data); + free(key->data); free(key); } @@ -1062,13 +1108,14 @@ load_identity(void) char *identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, NULL); size_t identity_key_public_len; unsigned char *identity_key_public = g_base64_decode(identity_key_public_b64, &identity_key_public_len); + g_free(identity_key_public_b64); omemo_ctx.identity_key_store.public = signal_buffer_create(identity_key_public, identity_key_public_len); char *identity_key_private_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, NULL); size_t identity_key_private_len; unsigned char *identity_key_private = g_base64_decode(identity_key_private_b64, &identity_key_private_len); + g_free(identity_key_private_b64); omemo_ctx.identity_key_store.private = signal_buffer_create(identity_key_private, identity_key_private_len); - signal_buffer_create(identity_key_private, identity_key_private_len); ec_public_key *public_key; curve_decode_point(&public_key, identity_key_public, identity_key_public_len, omemo_ctx.signal); @@ -1086,11 +1133,13 @@ load_identity(void) char *key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, keys[i], NULL); size_t key_len; unsigned char *key = g_base64_decode(key_b64, &key_len); + g_free(key_b64); signal_buffer *buffer = signal_buffer_create(key, key_len); - g_hash_table_insert(omemo_ctx.identity_key_store.trusted, keys[i], buffer); - free(key_b64); + g_free(key); + g_hash_table_insert(omemo_ctx.identity_key_store.trusted, strdup(keys[i]), buffer); } } + g_strfreev(keys); } static void @@ -1115,11 +1164,14 @@ load_sessions(void) char *record_b64 = g_key_file_get_string(omemo_ctx.sessions_keyfile, groups[i], keys[j], NULL); size_t record_len; unsigned char *record = g_base64_decode(record_b64, &record_len); + g_free(record_b64); signal_buffer *buffer = signal_buffer_create(record, record_len); + g_free(record); g_hash_table_insert(device_store, GINT_TO_POINTER(id), buffer); - free(record_b64); } + g_strfreev(keys); } + free(groups); } } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index e78bd09b..e9e9cf8d 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -9,7 +9,7 @@ typedef struct omemo_context_t omemo_context; typedef struct omemo_key { - const unsigned char *data; + unsigned char *data; size_t length; gboolean prekey; uint32_t device_id; @@ -18,7 +18,9 @@ typedef struct omemo_key { void omemo_init(void); void omemo_on_connect(ProfAccount *account); +void omemo_on_disconnect(void); void omemo_generate_crypto_materials(ProfAccount *account); +void omemo_key_free(omemo_key_t *key); uint32_t omemo_device_id(void); void omemo_identity_key(unsigned char **output, size_t *length); diff --git a/src/omemo/store.c b/src/omemo/store.c index 621c902c..4b20d29f 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -109,7 +109,10 @@ store_session(const signal_protocol_address *address, char *record_b64 = g_base64_encode(record, record_len); - g_key_file_set_string(omemo_sessions_keyfile(), address->name, g_strdup_printf("%d", address->device_id), record_b64); + char *device_id = g_strdup_printf("%d", address->device_id); + g_key_file_set_string(omemo_sessions_keyfile(), address->name, device_id, record_b64); + free(device_id); + g_free(record_b64); omemo_sessions_keyfile_save(); @@ -283,9 +286,10 @@ save_identity(const signal_protocol_address *address, uint8_t *key_data, char *key_b64 = g_base64_encode(key_data, key_len); g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_TRUST, node, key_b64); + g_free(key_b64); omemo_identity_keyfile_save(); - free(node); + g_free(node); return SG_SUCCESS; } @@ -294,13 +298,19 @@ int is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data) { + int ret; identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; char *node = g_strdup_printf("%s:%d", address->name, address->device_id); signal_buffer *buffer = signal_buffer_create(key_data, key_len); signal_buffer *original = g_hash_table_lookup(identity_key_store->trusted, node); + g_free(node); - return original != NULL && signal_buffer_compare(buffer, original) == 0; + ret = original != NULL && signal_buffer_compare(buffer, original) == 0; + + signal_buffer_free(buffer); + + return ret; } int diff --git a/src/xmpp/message.c b/src/xmpp/message.c index cc3d66bd..bc166111 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -405,6 +405,7 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, xmpp_stanza_add_child(body, body_text); xmpp_stanza_release(body_text); xmpp_stanza_add_child(message, body); + xmpp_stanza_release(body); if (state) { stanza_attach_state(ctx, message, state); diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 1a10e374..6c57781b 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -70,6 +70,10 @@ omemo_bundle_publish(void) signed_prekey_signature, signed_prekey_signature_length, prekeys, ids, lengths); + g_list_free_full(prekeys, free); + g_list_free(lengths); + g_list_free(ids); + Jid *jid = jid_create(connection_get_fulljid()); if (caps_jid_has_feature(jid->barejid, XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); @@ -157,7 +161,9 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons if (!prekey_text) { return 1; } - key->data = g_base64_decode(xmpp_stanza_get_text(prekey_text), &key->length); + char *prekey_b64 = xmpp_stanza_get_text(prekey_text); + key->data = g_base64_decode(prekey_b64, &key->length); + free(prekey_b64); key->prekey = TRUE; key->device_id = device_id; @@ -178,7 +184,9 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons return 1; } size_t signed_prekey_len; - unsigned char *signed_prekey_raw = g_base64_decode(xmpp_stanza_get_text(signed_prekey_text), &signed_prekey_len); + char *signed_prekey_b64 = xmpp_stanza_get_text(signed_prekey_text); + unsigned char *signed_prekey_raw = g_base64_decode(signed_prekey_b64, &signed_prekey_len); + free(signed_prekey_b64); xmpp_stanza_t *signed_prekey_signature = xmpp_stanza_get_child_by_name(bundle, "signedPreKeySignature"); if (!signed_prekey_signature) { @@ -189,7 +197,9 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons return 1; } size_t signed_prekey_signature_len; - unsigned char *signed_prekey_signature_raw = g_base64_decode(xmpp_stanza_get_text(signed_prekey_signature_text), &signed_prekey_signature_len); + char *signed_prekey_signature_b64 = xmpp_stanza_get_text(signed_prekey_signature_text); + unsigned char *signed_prekey_signature_raw = g_base64_decode(signed_prekey_signature_b64, &signed_prekey_signature_len); + free(signed_prekey_signature_b64); xmpp_stanza_t *identity_key = xmpp_stanza_get_child_by_name(bundle, "identityKey"); if (!identity_key) { @@ -200,11 +210,18 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons return 1; } size_t identity_key_len; - unsigned char *identity_key_raw = g_base64_decode(xmpp_stanza_get_text(identity_key_text), &identity_key_len); + char *identity_key_b64 = xmpp_stanza_get_text(identity_key_text); + unsigned char *identity_key_raw = g_base64_decode(identity_key_b64, &identity_key_len); + free(identity_key_b64); omemo_start_device_session(from, device_id, prekeys_list, signed_prekey_id, signed_prekey_raw, signed_prekey_len, signed_prekey_signature_raw, signed_prekey_signature_len, identity_key_raw, identity_key_len); + + g_list_free_full(prekeys_list, (GDestroyNotify)omemo_key_free); + g_free(signed_prekey_raw); + g_free(identity_key_raw); + g_free(signed_prekey_signature_raw); return 1; } @@ -233,23 +250,23 @@ omemo_receive_message(xmpp_stanza_t *const stanza) if (!iv) { return NULL; } - const char *iv_text = xmpp_stanza_get_text(iv); + char *iv_text = xmpp_stanza_get_text(iv); if (!iv_text) { return NULL; } size_t iv_len; - const unsigned char *iv_raw = g_base64_decode(iv_text, &iv_len); + unsigned char *iv_raw = g_base64_decode(iv_text, &iv_len); xmpp_stanza_t *payload = xmpp_stanza_get_child_by_name(encrypted, "payload"); if (!payload) { return NULL; } - const char *payload_text = xmpp_stanza_get_text(payload); + char *payload_text = xmpp_stanza_get_text(payload); if (!payload_text) { return NULL; } size_t payload_len; - const unsigned char *payload_raw = g_base64_decode(payload_text, &payload_len); + unsigned char *payload_raw = g_base64_decode(payload_text, &payload_len); GList *keys = NULL; xmpp_stanza_t *key_stanza; @@ -259,7 +276,7 @@ omemo_receive_message(xmpp_stanza_t *const stanza) } omemo_key_t *key = malloc(sizeof(omemo_key_t)); - const char *key_text = xmpp_stanza_get_text(key_stanza); + char *key_text = xmpp_stanza_get_text(key_stanza); if (!key_text) { goto skip; } @@ -271,6 +288,7 @@ omemo_receive_message(xmpp_stanza_t *const stanza) goto skip; } key->data = g_base64_decode(key_text, &key->length); + free(key_text); key->prekey = g_strcmp0(xmpp_stanza_get_attribute(key_stanza, "prekey"), "true") == 0; keys = g_list_append(keys, key); continue; @@ -285,6 +303,12 @@ skip: keys, payload_raw, payload_len, g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0); + g_list_free_full(keys, (GDestroyNotify)omemo_key_free); + g_free(iv_raw); + g_free(payload_raw); + g_free(iv_text); + g_free(payload_text); + return plaintext; } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 65a6e02b..b6992e82 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2274,7 +2274,9 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, xmpp_stanza_set_attribute(signed_prekey_public_stanza, "signedPreKeyId", "1"); xmpp_stanza_t *signed_prekey_public_stanza_text= xmpp_stanza_new(ctx); - xmpp_stanza_set_text(signed_prekey_public_stanza_text, g_base64_encode(signed_prekey, signed_prekey_length)); + char *signed_prekey_b64 = g_base64_encode(signed_prekey, signed_prekey_length); + xmpp_stanza_set_text(signed_prekey_public_stanza_text, signed_prekey_b64); + g_free(signed_prekey_b64); xmpp_stanza_add_child(signed_prekey_public_stanza, signed_prekey_public_stanza_text); xmpp_stanza_release(signed_prekey_public_stanza_text); @@ -2282,7 +2284,9 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, xmpp_stanza_set_name(signed_prekey_signature_stanza , "signedPreKeySignature"); xmpp_stanza_t *signed_prekey_signature_stanza_text= xmpp_stanza_new(ctx); - xmpp_stanza_set_text(signed_prekey_signature_stanza_text, g_base64_encode(signed_prekey_signature, signed_prekey_signature_length)); + char *signed_prekey_signature_b64 = g_base64_encode(signed_prekey_signature, signed_prekey_signature_length); + xmpp_stanza_set_text(signed_prekey_signature_stanza_text, signed_prekey_signature_b64); + g_free(signed_prekey_signature_b64); xmpp_stanza_add_child(signed_prekey_signature_stanza, signed_prekey_signature_stanza_text); xmpp_stanza_release(signed_prekey_signature_stanza_text); @@ -2290,7 +2294,9 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, xmpp_stanza_set_name(identity_key_stanza , "identityKey"); xmpp_stanza_t *identity_key_stanza_text= xmpp_stanza_new(ctx); - xmpp_stanza_set_text(identity_key_stanza_text, g_base64_encode(identity_key, identity_key_length)); + char *identity_key_b64 = g_base64_encode(identity_key, identity_key_length); + xmpp_stanza_set_text(identity_key_stanza_text, identity_key_b64); + g_free(identity_key_b64); xmpp_stanza_add_child(identity_key_stanza, identity_key_stanza_text); xmpp_stanza_release(identity_key_stanza_text); @@ -2306,7 +2312,9 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, g_free(id); xmpp_stanza_t *prekey_text = xmpp_stanza_new(ctx); - xmpp_stanza_set_text(prekey_text, g_base64_encode(p->data, GPOINTER_TO_INT(l->data))); + char *prekey_b64 = g_base64_encode(p->data, GPOINTER_TO_INT(l->data)); + xmpp_stanza_set_text(prekey_text, prekey_b64); + g_free(prekey_b64); xmpp_stanza_add_child(prekey, prekey_text); xmpp_stanza_add_child(prekeys_stanza, prekey); -- cgit 1.4.1-2-gfad0 From dadd0c0590603b76d2b08d6f62e29d1a5a0d50f0 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Mar 2019 19:12:15 +0140 Subject: Add missing preferences for OMEMO logs --- src/config/preferences.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config/preferences.c b/src/config/preferences.c index 7d0c91fc..65e7a64d 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1685,6 +1685,8 @@ _get_group(preference_t pref) return PREF_GROUP_MUC; case PREF_PLUGINS_SOURCEPATH: return PREF_GROUP_PLUGINS; + case PREF_OMEMO_LOG: + return PREF_GROUP_OMEMO; default: return NULL; } @@ -1899,6 +1901,8 @@ _get_key(preference_t pref) return "statusbar.chat"; case PREF_STATUSBAR_ROOM: return "statusbar.room"; + case PREF_OMEMO_LOG: + return "log"; default: return NULL; } @@ -2017,6 +2021,8 @@ _get_default_string(preference_t pref) return "user"; case PREF_STATUSBAR_ROOM: return "room"; + case PREF_OMEMO_LOG: + return "redact"; default: return NULL; } -- cgit 1.4.1-2-gfad0 From 306706bd2efa61f44a9234241d5de60c8e286abd Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 22 Mar 2019 19:20:23 +0140 Subject: Add OMEMO log command --- src/command/cmd_ac.c | 14 ++++++++++++++ src/command/cmd_defs.c | 4 ++++ src/command/cmd_funcs.c | 30 ++++++++++++++++++++++++++++++ src/command/cmd_funcs.h | 1 + 4 files changed, 49 insertions(+) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 8db9c035..86ef6ba9 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -159,6 +159,7 @@ static Autocomplete otr_ac; static Autocomplete otr_log_ac; static Autocomplete otr_policy_ac; static Autocomplete omemo_ac; +static Autocomplete omemo_log_ac; static Autocomplete connect_property_ac; static Autocomplete tls_property_ac; static Autocomplete alias_ac; @@ -578,12 +579,18 @@ cmd_ac_init(void) omemo_ac = autocomplete_new(); autocomplete_add(omemo_ac, "gen"); + autocomplete_add(omemo_ac, "log"); autocomplete_add(omemo_ac, "start"); autocomplete_add(omemo_ac, "end"); autocomplete_add(omemo_ac, "trust"); autocomplete_add(omemo_ac, "untrust"); autocomplete_add(omemo_ac, "fingerprint"); + omemo_log_ac = autocomplete_new(); + autocomplete_add(omemo_log_ac, "on"); + autocomplete_add(omemo_log_ac, "off"); + autocomplete_add(omemo_log_ac, "redact"); + connect_property_ac = autocomplete_new(); autocomplete_add(connect_property_ac, "server"); autocomplete_add(connect_property_ac, "port"); @@ -1063,6 +1070,7 @@ cmd_ac_reset(ProfWin *window) autocomplete_reset(otr_log_ac); autocomplete_reset(otr_policy_ac); autocomplete_reset(omemo_ac); + autocomplete_reset(omemo_log_ac); autocomplete_reset(connect_property_ac); autocomplete_reset(tls_property_ac); autocomplete_reset(alias_ac); @@ -1191,6 +1199,7 @@ cmd_ac_uninit(void) autocomplete_free(otr_log_ac); autocomplete_free(otr_policy_ac); autocomplete_free(omemo_ac); + autocomplete_free(omemo_log_ac); autocomplete_free(connect_property_ac); autocomplete_free(tls_property_ac); autocomplete_free(alias_ac); @@ -2144,6 +2153,11 @@ _omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous) } } + found = autocomplete_param_with_ac(input, "/omemo log", omemo_log_ac, TRUE, previous); + if (found) { + return found; + } + found = autocomplete_param_with_ac(input, "/omemo", omemo_ac, TRUE, previous); if (found) { return found; diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 2d4ae061..311b404e 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2334,6 +2334,7 @@ static struct cmd_t command_defs[] = parse_args, 1, 3, NULL, CMD_SUBFUNCS( { "gen", cmd_omemo_gen }, + { "log", cmd_omemo_log }, { "start", cmd_omemo_start }, { "end", cmd_omemo_end }, { "trust", cmd_omemo_trust }, @@ -2345,6 +2346,7 @@ static struct cmd_t command_defs[] = CMD_TAG_UI) CMD_SYN( "/omemo gen", + "/omemo log on|off|redact", "/omemo start []", "/omemo trust [] ", "/omemo end", @@ -2355,6 +2357,8 @@ static struct cmd_t command_defs[] = { "gen", "Generate OMEMO crytographic materials for current account." }, { "start []", "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]. This is the default." }, { "fingerprint", "Show current device fingerprint." }) CMD_EXAMPLES( "/omemo gen", diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 49e20744..d254960d 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7995,6 +7995,36 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) #endif } +gboolean +cmd_omemo_log(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + char *choice = args[1]; + if (g_strcmp0(choice, "on") == 0) { + prefs_set_string(PREF_OMEMO_LOG, "on"); + cons_show("OMEMO messages will be logged as plaintext."); + if (!prefs_get_boolean(PREF_CHLOG)) { + cons_show("Chat logging is currently disabled, use '/chlog on' to enable."); + } + } else if (g_strcmp0(choice, "off") == 0) { + prefs_set_string(PREF_OMEMO_LOG, "off"); + cons_show("OMEMO message logging disabled."); + } else if (g_strcmp0(choice, "redact") == 0) { + prefs_set_string(PREF_OMEMO_LOG, "redact"); + cons_show("OMEMO messages will be logged as '[redacted]'."); + if (!prefs_get_boolean(PREF_CHLOG)) { + cons_show("Chat logging is currently disabled, use '/chlog on' to enable."); + } + } else { + cons_bad_cmd_usage(command); + } + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} + gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args) { diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 21e7a51f..3f3385a5 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -215,6 +215,7 @@ gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args) gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_log(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); -- cgit 1.4.1-2-gfad0 From 2fd2ca208cf9c191453f923722df3b9630ba9ff5 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Mar 2019 07:17:57 +0140 Subject: Use connection_support to check for publish-options support --- src/xmpp/omemo.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 6c57781b..4fcca699 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -23,11 +23,9 @@ omemo_devicelist_publish(GList *device_list) xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, device_list); - Jid *jid = jid_create(connection_get_fulljid()); - if (caps_jid_has_feature(jid->barejid, XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { + if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); } - jid_destroy(jid); iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -74,11 +72,9 @@ omemo_bundle_publish(void) g_list_free(lengths); g_list_free(ids); - Jid *jid = jid_create(connection_get_fulljid()); - if (caps_jid_has_feature(jid->barejid, XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { + if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); } - jid_destroy(jid); iq_send_stanza(iq); xmpp_stanza_release(iq); -- cgit 1.4.1-2-gfad0 From 3d8f47a72416ff4b1ca64bf66071a62e4661d036 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Mar 2019 18:30:46 +0140 Subject: Use /omemo fingerprint to show contact fingerprints Don't print fingerprints when they are received --- src/command/cmd_ac.c | 5 ++++ src/command/cmd_defs.c | 4 +-- src/command/cmd_funcs.c | 46 +++++++++++++++++++++++++++++--- src/omemo/omemo.c | 71 ++++++++++++++++++++++++++++++------------------- src/omemo/omemo.h | 2 ++ 5 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 86ef6ba9..9584543e 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -2153,6 +2153,11 @@ _omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous) } } + found = autocomplete_param_with_func(input, "/omemo fingerprint", roster_contact_autocomplete, previous); + if (found) { + return found; + } + found = autocomplete_param_with_ac(input, "/omemo log", omemo_log_ac, TRUE, previous); if (found) { return found; diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 311b404e..5a3e6873 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2350,7 +2350,7 @@ static struct cmd_t command_defs[] = "/omemo start []", "/omemo trust [] ", "/omemo end", - "/omemo fingerprint") + "/omemo fingerprint []") CMD_DESC( "Omemo commands to manage keys, and perform encryption during chat sessions.") CMD_ARGS( @@ -2359,7 +2359,7 @@ static struct cmd_t command_defs[] = { "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]. This is the default." }, - { "fingerprint", "Show current device fingerprint." }) + { "fingerprint", "Show contact fingerprints." }) CMD_EXAMPLES( "/omemo gen", "/omemo start buddy@buddychat.org", diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index d254960d..fcbb10b8 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8080,9 +8080,49 @@ cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args) return TRUE; } - char *fingerprint = omemo_own_fingerprint(TRUE); - cons_show("%s", fingerprint); - free(fingerprint); + Jid *jid; + if (!args[1]) { + if (window->type == WIN_CONSOLE) { + char *fingerprint = omemo_own_fingerprint(TRUE); + cons_show("Your OMEMO fingerprint: %s", fingerprint); + free(fingerprint); + return TRUE; + } else if (window->type == WIN_CHAT) { + ProfChatWin *chatwin = (ProfChatWin*)window; + jid = jid_create(chatwin->barejid); + } else { + win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to print fingerprint without providing the contact."); + return TRUE; + } + } else { + jid = jid_create(args[1]); + if (!jid) { + cons_show("%s is not a valid jid", args[1]); + return TRUE; + } + } + + GList *fingerprints = omemo_known_device_identities(jid->barejid); + GList *fingerprint; + + if (!fingerprints) { + win_println(window, THEME_DEFAULT, '-', "There is no known fingerprints for %s", jid->barejid); + return TRUE; + } + + for (fingerprint = fingerprints; fingerprint != NULL; fingerprint = fingerprint->next) { + char *formatted_fingerprint = omemo_format_fingerprint(fingerprint->data); + gboolean trusted = omemo_is_trusted_identity(jid->barejid, fingerprint->data); + + win_println(window, THEME_DEFAULT, '-', "%s's OMEMO fingerprint: %s%s", jid->barejid, formatted_fingerprint, trusted ? " (trusted)" : ""); + + free(formatted_fingerprint); + } + + g_list_free(fingerprints); + + win_println(window, THEME_DEFAULT, '-', "You can trust it with '/omemo trust '"); + win_println(window, THEME_DEFAULT, '-', "You can untrust it with '/omemo untrust '"); return TRUE; #else diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 8ec0554f..01eb6b67 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -496,32 +496,6 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, gboolean trusted = is_trusted_identity(&address, (uint8_t *)identity_key_raw, identity_key_len, &omemo_ctx.identity_key_store); - Jid *ownjid = jid_create(connection_get_fulljid()); - if (g_strcmp0(jid, ownjid->barejid) == 0) { - char *fingerprint = omemo_fingerprint(identity_key, TRUE); - - cons_show("Available device identity for %s: %s%s", ownjid->barejid, fingerprint, trusted ? " (trusted)" : ""); - if (trusted) { - cons_show("You can untrust it with '/omemo untrust %s '", ownjid->barejid); - } else { - cons_show("You can trust it with '/omemo trust %s '", ownjid->barejid); - } - free(fingerprint); - } - - ProfChatWin *chatwin = wins_get_chat(jid); - if (chatwin) { - char *fingerprint = omemo_fingerprint(identity_key, TRUE); - - win_println((ProfWin *)chatwin, THEME_DEFAULT, '-', "Available device identity: %s%s", fingerprint, trusted ? " (trusted)" : ""); - if (trusted) { - win_println((ProfWin *)chatwin, THEME_DEFAULT, '-', "You can untrust it with '/omemo untrust '"); - } else { - win_println((ProfWin *)chatwin, THEME_DEFAULT, '-', "You can trust it with '/omemo trust '"); - } - free(fingerprint); - } - if (!trusted) { goto out; } @@ -570,7 +544,6 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, out: SIGNAL_UNREF(identity_key); - jid_destroy(ownjid); } char * @@ -884,6 +857,50 @@ omemo_own_fingerprint(gboolean formatted) return omemo_fingerprint(identity, formatted); } +GList * +omemo_known_device_identities(const char *const jid) +{ + GHashTable *known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid); + if (!known_identities) { + return NULL; + } + + return g_hash_table_get_keys(known_identities); +} + +gboolean +omemo_is_trusted_identity(const char *const jid, const char *const fingerprint) +{ + 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; + } + + signal_protocol_address address = { + .name = jid, + .name_len = strlen(jid), + .device_id = GPOINTER_TO_INT(device_id), + }; + + size_t fingerprint_len; + unsigned char *fingerprint_raw = omemo_fingerprint_decode(fingerprint, &fingerprint_len); + unsigned char djb_type[] = {'\x05'}; + signal_buffer *buffer = signal_buffer_create(djb_type, 1); + 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); + + free(fingerprint_raw); + signal_buffer_free(buffer); + + return trusted; +} + static char * omemo_fingerprint(ec_public_key *identity, gboolean formatted) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index e9e9cf8d..ec48e183 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -36,6 +36,8 @@ char *omemo_format_fingerprint(const char *const fingerprint); char *omemo_own_fingerprint(gboolean formatted); 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); void omemo_start_session(const char *const barejid); void omemo_start_muc_sessions(const char *const roomjid); -- cgit 1.4.1-2-gfad0 From 678bff9169c81ed20c38698e132d7c8c6118763b Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Mar 2019 19:07:36 +0140 Subject: Add fingerprint autocompletion --- src/command/cmd_ac.c | 26 ++++++++++++++++++++++++++ src/omemo/omemo.c | 22 +++++++++++++++++++++- src/omemo/omemo.h | 2 ++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 9584543e..c700e51b 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -57,6 +57,10 @@ #include "pgp/gpg.h" #endif +#ifdef HAVE_OMEMO +#include "omemo/omemo.h" +#endif + static char* _sub_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _notify_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _theme_autocomplete(ProfWin *window, const char *const input, gboolean previous); @@ -999,6 +1003,9 @@ cmd_ac_reset(ProfWin *window) presence_reset_sub_request_search(); #ifdef HAVE_LIBGPGME p_gpg_autocomplete_key_reset(); +#endif +#ifdef HAVE_OMEMO + omemo_fingerprint_autocomplete_reset(); #endif autocomplete_reset(help_ac); autocomplete_reset(help_commands_ac); @@ -2158,6 +2165,25 @@ _omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous) return found; } +#ifdef HAVE_OMEMO + if (window->type == WIN_CHAT) { + found = autocomplete_param_with_func(input, "/omemo trust", omemo_fingerprint_autocomplete, previous); + if (found) { + return found; + } + } else { + found = autocomplete_param_with_func(input, "/omemo trust", roster_contact_autocomplete, previous); + if (found) { + return found; + } + + found = autocomplete_param_no_with_func(input, "/omemo trust", 4, omemo_fingerprint_autocomplete, previous); + if (found) { + return found; + } + } +#endif + found = autocomplete_param_with_ac(input, "/omemo log", omemo_log_ac, TRUE, previous); if (found) { return found; diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 01eb6b67..1f48224e 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -63,6 +63,7 @@ struct omemo_context_t { GString *sessions_filename; GKeyFile *sessions_keyfile; GHashTable *known_devices; + Autocomplete fingerprint_ac; }; static omemo_context omemo_ctx; @@ -163,6 +164,8 @@ omemo_init(void) 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_free); + + omemo_ctx.fingerprint_ac = autocomplete_new(); } void @@ -1114,6 +1117,18 @@ omemo_key_free(omemo_key_t *key) free(key); } +char* +omemo_fingerprint_autocomplete(const char *const search_str, gboolean previous) +{ + return autocomplete_complete(omemo_ctx.fingerprint_ac, search_str, FALSE, previous); +} + +void +omemo_fingerprint_autocomplete_reset(void) +{ + autocomplete_reset(omemo_ctx.fingerprint_ac); +} + static void load_identity(void) { @@ -1203,7 +1218,12 @@ cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key * char *fingerprint = omemo_fingerprint(identity, FALSE); log_info("OMEMO: cache identity for %s:%d: %s", jid, device_id, fingerprint); - g_hash_table_insert(known_identities, fingerprint, GINT_TO_POINTER(device_id)); + g_hash_table_insert(known_identities, strdup(fingerprint), GINT_TO_POINTER(device_id)); + + char *formatted_fingerprint = omemo_format_fingerprint(fingerprint); + autocomplete_add(omemo_ctx.fingerprint_ac, formatted_fingerprint); + free(formatted_fingerprint); + free(fingerprint); } static void diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index ec48e183..bbd59b77 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -38,6 +38,8 @@ 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); +char *omemo_fingerprint_autocomplete(const char *const search_str, gboolean previous); +void omemo_fingerprint_autocomplete_reset(void); void omemo_start_session(const char *const barejid); void omemo_start_muc_sessions(const char *const roomjid); -- cgit 1.4.1-2-gfad0 From bc16ba1669b5a8fb0d2b449b2f2be8149025d70b Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Mar 2019 19:19:18 +0140 Subject: Temporarly disable check for publish-options support --- src/xmpp/omemo.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 4fcca699..98509ff1 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -23,9 +23,7 @@ omemo_devicelist_publish(GList *device_list) xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, device_list); - if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { - stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); - } + stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -72,9 +70,7 @@ omemo_bundle_publish(void) g_list_free(lengths); g_list_free(ids); - if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { - stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); - } + stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); iq_send_stanza(iq); xmpp_stanza_release(iq); -- cgit 1.4.1-2-gfad0 From ba9679a706a1c3d4d37b45da707978d83a463b2a Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 26 Mar 2019 17:10:46 +0100 Subject: Print fingerprint after trust and untrust command --- src/command/cmd_funcs.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index fcbb10b8..eb550812 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8176,6 +8176,23 @@ cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args) omemo_trust(barejid, fingerprint); + char *unformatted_fingerprint = malloc(strlen(fingerprint)); + int i; + int j; + for (i = 0, j = 0; fingerprint[i] != '\0'; i++) { + if (!g_ascii_isxdigit(fingerprint[i])) { + continue; + } + unformatted_fingerprint[j++] = fingerprint[i]; + } + + unformatted_fingerprint[j] = '\0'; + gboolean trusted = omemo_is_trusted_identity(barejid, unformatted_fingerprint); + + win_println(window, THEME_DEFAULT, '-', "%s's OMEMO fingerprint: %s%s", barejid, fingerprint, trusted ? " (trusted)" : ""); + + free(unformatted_fingerprint); + return TRUE; #else cons_show("This version of Profanity has not been built with OMEMO support enabled"); @@ -8228,6 +8245,23 @@ cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args) omemo_untrust(barejid, fingerprint); + char *unformatted_fingerprint = malloc(strlen(fingerprint)); + int i; + int j; + for (i = 0, j = 0; fingerprint[i] != '\0'; i++) { + if (!g_ascii_isxdigit(fingerprint[i])) { + continue; + } + unformatted_fingerprint[j++] = fingerprint[i]; + } + + unformatted_fingerprint[j] = '\0'; + gboolean trusted = omemo_is_trusted_identity(barejid, unformatted_fingerprint); + + win_println(window, THEME_DEFAULT, '-', "%s's OMEMO fingerprint: %s%s", barejid, fingerprint, trusted ? " (trusted)" : ""); + + free(unformatted_fingerprint); + return TRUE; #else cons_show("This version of Profanity has not been built with OMEMO support enabled"); -- cgit 1.4.1-2-gfad0 From 4a1c3c8c1ef8c86e28afe34848b2b9d87489c2f1 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 26 Mar 2019 22:24:26 +0100 Subject: Show all device fingerprint for current account --- src/command/cmd_funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index eb550812..369a992f 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8086,7 +8086,7 @@ cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args) char *fingerprint = omemo_own_fingerprint(TRUE); cons_show("Your OMEMO fingerprint: %s", fingerprint); free(fingerprint); - return TRUE; + jid = jid_create(connection_get_fulljid()); } else if (window->type == WIN_CHAT) { ProfChatWin *chatwin = (ProfChatWin*)window; jid = jid_create(chatwin->barejid); -- cgit 1.4.1-2-gfad0 From daf84ced0cbead11c930017df1bd713b02776ee3 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 26 Mar 2019 23:19:53 +0100 Subject: Add required OMEMO stub for unit-test --- Makefile.am | 4 +++ tests/unittests/log/stub_log.c | 8 +++-- tests/unittests/omemo/stub_omemo.c | 61 ++++++++++++++++++++++++++++++++++++++ tests/unittests/ui/stub_ui.c | 3 +- 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 tests/unittests/omemo/stub_omemo.c diff --git a/Makefile.am b/Makefile.am index ca60ac95..e161d854 100644 --- a/Makefile.am +++ b/Makefile.am @@ -175,6 +175,9 @@ omemo_sources = \ src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c \ src/omemo/store.h src/omemo/store.c src/xmpp/omemo.h src/xmpp/omemo.c +omemo_unittest_sources = \ + tests/unittests/omemo/stub_omemo.c + if BUILD_PYTHON_API core_sources += $(python_sources) unittest_sources += $(python_sources) @@ -213,6 +216,7 @@ endif if BUILD_OMEMO core_sources += $(omemo_sources) +unittest_sources += $(omemo_unittest_sources) endif AM_CFLAGS = @AM_CFLAGS@ -I$(srcdir)/src diff --git a/tests/unittests/log/stub_log.c b/tests/unittests/log/stub_log.c index e7d744b3..dfbec2bd 100644 --- a/tests/unittests/log/stub_log.c +++ b/tests/unittests/log/stub_log.c @@ -58,10 +58,12 @@ void chat_log_init(void) {} void chat_log_msg_out(const char * const barejid, const char * const msg) {} void chat_log_otr_msg_out(const char * const barejid, const char * const msg) {} void chat_log_pgp_msg_out(const char * const barejid, const char * const msg) {} +void chat_log_omemo_msg_out(const char *const barejid, const char *const msg) {} void chat_log_msg_in(const char * const barejid, const char * const msg, GDateTime *timestamp) {} void chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted, GDateTime *timestamp) {} void chat_log_pgp_msg_in(const char * const barejid, const char * const msg, GDateTime *timestamp) {} +void chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp) {} void chat_log_close(void) {} GSList * chat_log_get_previous(const gchar * const login, @@ -71,5 +73,7 @@ GSList * chat_log_get_previous(const gchar * const login, } void groupchat_log_init(void) {} -void groupchat_log_chat(const gchar * const login, const gchar * const room, - const gchar * const nick, const gchar * const msg) {} +void groupchat_log_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg) {} +void groupchat_log_msg_out(const gchar *const room, const gchar *const msg) {} +void groupchat_log_omemo_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg) {} +void groupchat_log_omemo_msg_out(const gchar *const room, const gchar *const msg) {} diff --git a/tests/unittests/omemo/stub_omemo.c b/tests/unittests/omemo/stub_omemo.c new file mode 100644 index 00000000..200226b0 --- /dev/null +++ b/tests/unittests/omemo/stub_omemo.c @@ -0,0 +1,61 @@ +#include + +#include "config/account.h" +#include "ui/ui.h" + +void omemo_init(void) {} + +char* +omemo_fingerprint_autocomplete(const char *const search_str, gboolean previous) +{ + return NULL; +} + +void omemo_fingerprint_autocomplete_reset(void) {} + +char * +omemo_format_fingerprint(const char *const fingerprint) +{ + return NULL; +} + +void omemo_generate_crypto_materials(ProfAccount *account) {} + +gboolean +omemo_is_trusted_identity(const char *const jid, const char *const fingerprint) +{ + return TRUE; +} + +GList * +omemo_known_device_identities(const char *const jid) +{ + return NULL; +} + +gboolean +omemo_loaded(void) +{ + return TRUE; +} + +void omemo_on_connect(ProfAccount *account) {} +void omemo_on_disconnect(void) {} + +char * +omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc) +{ + return NULL; +} + +char * +omemo_own_fingerprint(gboolean formatted) +{ + return NULL; +} + +void omemo_start_muc_sessions(const char *const roomjid) {} +void omemo_start_session(const char *const barejid) {} +void omemo_trust(const char *const jid, const char *const fingerprint_formatted) {} +void omemo_untrust(const char *const jid, const char *const fingerprint_formatted) {} +void omemo_devicelist_publish(GList *device_list) {} diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 855dca22..e08c3ce1 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -190,7 +190,8 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char const char * const affiliation, const char * const actor, const char * const reason) {} void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char * const presence) {} void mucwin_history(ProfMucWin *mucwin, const char * const nick, GDateTime *timestamp, const char * const message) {} -void mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers) {} +void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers, prof_enc_t enc_mode) {} +void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, prof_enc_t enc_mode) {} void mucwin_subject(ProfMucWin *mucwin, const char * const nick, const char * const subject) {} void mucwin_requires_config(ProfMucWin *mucwin) {} void ui_room_destroy(const char * const roomjid) {} -- cgit 1.4.1-2-gfad0 From 2015ba201d446a181c240f4cc957b99af4d9ba1d Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 27 Mar 2019 13:37:09 +0100 Subject: Ensure encrypted carbon of own message are marked as encrypted --- src/event/server_events.c | 40 ++++++++++++++++++++++++++++++++++++++-- src/event/server_events.h | 2 +- src/ui/chatwin.c | 2 ++ src/xmpp/message.c | 2 +- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index fd719cc1..023e8a13 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -387,7 +387,7 @@ sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTim } void -sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message) +sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean omemo) { ProfChatWin *chatwin = wins_get_chat(barejid); if (!chatwin) { @@ -397,6 +397,7 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message) chat_state_active(chatwin->state); #ifdef HAVE_LIBGPGME +#ifndef HAVE_OMEMO if (pgp_message) { char *decrypted = p_gpg_decrypt(pgp_message); if (decrypted) { @@ -407,9 +408,44 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message) } else { chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN); } -#else + return; +#endif +#endif + +#ifndef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (omemo) { + chatwin_outgoing_carbon(chatwin, message, PROF_MSG_OMEMO); + } else { + chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN); + } + return; +#endif +#endif + +#ifdef HAVE_LIBGPGME +#ifdef HAVE_OMEMO + if (omemo) { + chatwin_outgoing_carbon(chatwin, message, PROF_MSG_OMEMO); + } else if (pgp_message) { + char *decrypted = p_gpg_decrypt(pgp_message); + if (decrypted) { + chatwin_outgoing_carbon(chatwin, decrypted, PROF_MSG_PGP); + } else { + chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN); + } + } else { + chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN); + } + return; +#endif +#endif + +#ifndef HAVE_LIBGPGME +#ifndef HAVE_OMEMO chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN); #endif +#endif } #ifdef HAVE_LIBGPGME diff --git a/src/event/server_events.h b/src/event/server_events.h index 0fa9989f..cf437f9a 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -73,7 +73,7 @@ void sv_ev_room_occupent_kicked(const char *const room, const char *const nick, void sv_ev_room_banned(const char *const room, const char *const actor, const char *const reason); void sv_ev_room_occupent_banned(const char *const room, const char *const nick, const char *const actor, const char *const reason); -void sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message); +void sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean omemo); void sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message, gboolean omemo); void sv_ev_xmpp_stanza(const char *const msg); void sv_ev_muc_self_online(const char *const room, const char *const nick, gboolean config_required, diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index c7acce52..5064b194 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -324,6 +324,8 @@ chatwin_outgoing_carbon(ProfChatWin *chatwin, const char *const message, prof_en char enc_char = '-'; if (enc_mode == PROF_MSG_PGP) { enc_char = prefs_get_pgp_char(); + } else if (enc_mode == PROF_MSG_OMEMO) { + enc_char = prefs_get_omemo_char(); } ProfWin *window = (ProfWin*)chatwin; diff --git a/src/xmpp/message.c b/src/xmpp/message.c index bc166111..bc91ef3b 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -932,7 +932,7 @@ _handle_carbons(xmpp_stanza_t *const stanza) // else treat as a sent message } else { - sv_ev_outgoing_carbon(jid_to->barejid, message_txt, enc_message); + sv_ev_outgoing_carbon(jid_to->barejid, message_txt, enc_message, omemo); } xmpp_ctx_t *ctx = connection_get_ctx(); -- cgit 1.4.1-2-gfad0 From 075d4cf35b4fce6d623822a63ae0abfbfc78b965 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 27 Mar 2019 15:52:48 +0100 Subject: Don't use glib 2.58 func --- src/omemo/omemo.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 1f48224e..1efe0296 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1082,9 +1082,7 @@ static gboolean handle_own_device_list(const char *const jid, GList *device_list) { if (!g_list_find(device_list, GINT_TO_POINTER(omemo_ctx.device_id))) { - gpointer original_jid; - g_hash_table_steal_extended(omemo_ctx.device_list, jid, &original_jid, NULL); - free(original_jid); + device_list = g_list_copy(device_list); device_list = g_list_append(device_list, GINT_TO_POINTER(omemo_ctx.device_id)); g_hash_table_insert(omemo_ctx.device_list, strdup(jid), device_list); omemo_devicelist_publish(device_list); -- cgit 1.4.1-2-gfad0 From 904528b490e3e9e0d8ae0ecc8e8ec4126238c121 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 27 Mar 2019 17:15:59 +0100 Subject: Ensure signal context is built on connection We can't keep it between two connection because signal context is specific to a given account. --- src/omemo/omemo.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 1efe0296..9e46058e 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -72,21 +72,6 @@ void omemo_init(void) { log_info("OMEMO: initialising"); - signal_crypto_provider crypto_provider = { - .random_func = omemo_random_func, - .hmac_sha256_init_func = omemo_hmac_sha256_init_func, - .hmac_sha256_update_func = omemo_hmac_sha256_update_func, - .hmac_sha256_final_func = omemo_hmac_sha256_final_func, - .hmac_sha256_cleanup_func = omemo_hmac_sha256_cleanup_func, - .sha512_digest_init_func = omemo_sha512_digest_init_func, - .sha512_digest_update_func = omemo_sha512_digest_update_func, - .sha512_digest_final_func = omemo_sha512_digest_final_func, - .sha512_digest_cleanup_func = omemo_sha512_digest_cleanup_func, - .encrypt_func = omemo_encrypt_func, - .decrypt_func = omemo_decrypt_func, - .user_data = NULL - }; - if (omemo_crypto_init() != 0) { cons_show("Error initializing OMEMO crypto"); } @@ -95,6 +80,14 @@ omemo_init(void) pthread_mutexattr_settype(&omemo_ctx.attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&omemo_ctx.lock, &omemo_ctx.attr); + omemo_ctx.fingerprint_ac = autocomplete_new(); +} + +void +omemo_on_connect(ProfAccount *account) +{ + GError *error = NULL; + if (signal_context_create(&omemo_ctx.signal, &omemo_ctx) != 0) { cons_show("Error initializing OMEMO context"); return; @@ -104,6 +97,21 @@ omemo_init(void) cons_show("Error initializing OMEMO log"); } + signal_crypto_provider crypto_provider = { + .random_func = omemo_random_func, + .hmac_sha256_init_func = omemo_hmac_sha256_init_func, + .hmac_sha256_update_func = omemo_hmac_sha256_update_func, + .hmac_sha256_final_func = omemo_hmac_sha256_final_func, + .hmac_sha256_cleanup_func = omemo_hmac_sha256_cleanup_func, + .sha512_digest_init_func = omemo_sha512_digest_init_func, + .sha512_digest_update_func = omemo_sha512_digest_update_func, + .sha512_digest_final_func = omemo_sha512_digest_final_func, + .sha512_digest_cleanup_func = omemo_sha512_digest_cleanup_func, + .encrypt_func = omemo_encrypt_func, + .decrypt_func = omemo_decrypt_func, + .user_data = NULL + }; + if (signal_context_set_crypto_provider(omemo_ctx.signal, &crypto_provider) != 0) { cons_show("Error initializing OMEMO crypto"); return; @@ -166,12 +174,7 @@ omemo_init(void) omemo_ctx.known_devices = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_hash_table_free); omemo_ctx.fingerprint_ac = autocomplete_new(); -} -void -omemo_on_connect(ProfAccount *account) -{ - GError *error = NULL; char *omemodir = files_get_data_path(DIR_OMEMO); GString *basedir = g_string_new(omemodir); free(omemodir); -- cgit 1.4.1-2-gfad0 From 562302846af07cb7b7bde894741840419a6b42dd Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 28 Mar 2019 23:30:28 +0100 Subject: Handle bundle publication error on publish-options We try to reconfigure node and publish again. If it fails again then we give up. --- src/omemo/omemo.c | 4 +- src/xmpp/omemo.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/xmpp/omemo.h | 2 +- src/xmpp/stanza.c | 65 ++++++++++++++++++++++++++++---- src/xmpp/stanza.h | 7 +++- 5 files changed, 166 insertions(+), 23 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 9e46058e..580416f8 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -314,7 +314,7 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) g_hash_table_insert(omemo_ctx.device_list_handler, strdup(account->jid), handle_own_device_list); omemo_devicelist_request(account->jid); - omemo_bundle_publish(); + omemo_bundle_publish(true); } void @@ -791,7 +791,7 @@ omemo_on_message_recv(const char *const from_jid, uint32_t sid, SIGNAL_UNREF(new_pre_key); SIGNAL_UNREF(message); SIGNAL_UNREF(ec_pair); - omemo_bundle_publish(); + omemo_bundle_publish(true); if (res == 0) { /* Start a new session */ diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 98509ff1..66b0ebcc 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -1,13 +1,18 @@ #include +#include "log.h" #include "xmpp/connection.h" -#include "xmpp/message.h" +#include "xmpp/form.h" #include "xmpp/iq.h" +#include "xmpp/message.h" #include "xmpp/stanza.h" #include "omemo/omemo.h" static int _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata); +static int _omemo_bundle_publish_result(xmpp_stanza_t *const stanza, void *const userdata); +static int _omemo_bundle_publish_configure(xmpp_stanza_t *const stanza, void *const userdata); +static int _omemo_bundle_publish_configure_result(xmpp_stanza_t *const stanza, void *const userdata); void omemo_devicelist_subscribe(void) @@ -45,7 +50,7 @@ omemo_devicelist_request(const char * const jid) } void -omemo_bundle_publish(void) +omemo_bundle_publish(gboolean first) { xmpp_ctx_t * const ctx = connection_get_ctx(); unsigned char *identity_key = NULL; @@ -61,10 +66,11 @@ omemo_bundle_publish(void) omemo_signed_prekey_signature(&signed_prekey_signature, &signed_prekey_signature_length); omemo_prekeys(&prekeys, &ids, &lengths); - xmpp_stanza_t *iq = stanza_create_omemo_bundle_publish(ctx, omemo_device_id(), - identity_key, identity_key_length, signed_prekey, signed_prekey_length, - signed_prekey_signature, signed_prekey_signature_length, - prekeys, ids, lengths); + char *id = connection_create_stanza_id("omemo_bundle_publish"); + xmpp_stanza_t *iq = stanza_create_omemo_bundle_publish(ctx, id, + omemo_device_id(), identity_key, identity_key_length, signed_prekey, + signed_prekey_length, signed_prekey_signature, + signed_prekey_signature_length, prekeys, ids, lengths); g_list_free_full(prekeys, free); g_list_free(lengths); @@ -72,12 +78,15 @@ omemo_bundle_publish(void) stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); + iq_id_handler_add(id, _omemo_bundle_publish_result, NULL, GINT_TO_POINTER(first)); + iq_send_stanza(iq); - xmpp_stanza_release(iq); + xmpp_stanza_release(iq); free(identity_key); free(signed_prekey); free(signed_prekey_signature); + free(id); } void @@ -322,19 +331,19 @@ _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) } if (!root) { - return 1; + return 0; } xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(root, "items"); if (!items) { - return 1; + return 0; } xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); if (item) { xmpp_stanza_t *list = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO); if (!list) { - return 1; + return 0; } xmpp_stanza_t *device; @@ -345,5 +354,85 @@ _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) } omemo_set_device_list(from, device_list); - return 1; + return 0; +} + +static int +_omemo_bundle_publish_result(xmpp_stanza_t *const stanza, void *const userdata) +{ + const char *type = xmpp_stanza_get_type(stanza); + + if (g_strcmp0(type, STANZA_TYPE_ERROR) != 0) { + return 0; + } + + if (!GPOINTER_TO_INT(userdata)) { + log_error("OMEMO: definitely cannot publish bundle with an open access model"); + return 0; + } + + log_info("OMEMO: cannot publish bundle with open access model, trying to configure node"); + xmpp_ctx_t * const ctx = connection_get_ctx(); + Jid *jid = jid_create(connection_get_fulljid()); + char *id = connection_create_stanza_id("omemo_bundle_node_configure_request"); + char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, omemo_device_id()); + xmpp_stanza_t *iq = stanza_create_pubsub_configure_request(ctx, id, jid->barejid, node); + g_free(node); + + iq_id_handler_add(id, _omemo_bundle_publish_configure, NULL, userdata); + + iq_send_stanza(iq); + + xmpp_stanza_release(iq); + free(id); + jid_destroy(jid); + return 0; +} + +static int +_omemo_bundle_publish_configure(xmpp_stanza_t *const stanza, void *const userdata) +{ + /* TODO handle error */ + xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_name(stanza, "pubsub"); + xmpp_stanza_t *configure = xmpp_stanza_get_child_by_name(pubsub, STANZA_NAME_CONFIGURE); + xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(configure, "x"); + + DataForm* form = form_create(x); + char *tag = g_hash_table_lookup(form->var_to_tag, "pubsub#access_model"); + if (!tag) { + log_info("OMEMO: cannot configure bundle to an open access model"); + return 0; + } + form_set_value(form, tag, "open"); + + xmpp_ctx_t * const ctx = connection_get_ctx(); + Jid *jid = jid_create(connection_get_fulljid()); + char *id = connection_create_stanza_id("omemo_bundle_node_configure_submit"); + char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, omemo_device_id()); + xmpp_stanza_t *iq = stanza_create_pubsub_configure_submit(ctx, id, jid->barejid, node, form); + g_free(node); + + iq_id_handler_add(id, _omemo_bundle_publish_configure_result, NULL, userdata); + + iq_send_stanza(iq); + + xmpp_stanza_release(iq); + free(id); + jid_destroy(jid); + return 0; +} + +static int +_omemo_bundle_publish_configure_result(xmpp_stanza_t *const stanza, void *const userdata) +{ + const char *type = xmpp_stanza_get_type(stanza); + + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + log_error("OMEMO: cannot configure bundle to an open access model"); + return 0; + } + + omemo_bundle_publish(TRUE); + + return 0; } diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index 81c3b979..f1fff7b7 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -5,7 +5,7 @@ void omemo_devicelist_subscribe(void); void omemo_devicelist_publish(GList *device_list); void omemo_devicelist_request(const char * const jid); -void omemo_bundle_publish(void); +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); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index b6992e82..f4fd8aa8 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2156,7 +2156,7 @@ stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const char *const id, xmpp_stanza_t *items = xmpp_stanza_new(ctx); xmpp_stanza_set_name(items, "items"); - xmpp_stanza_set_attribute(items, "node", STANZA_NS_OMEMO_DEVICELIST); + xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, STANZA_NS_OMEMO_DEVICELIST); xmpp_stanza_add_child(pubsub, items); xmpp_stanza_add_child(iq, pubsub); @@ -2180,7 +2180,7 @@ stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid) xmpp_stanza_t *subscribe = xmpp_stanza_new(ctx); xmpp_stanza_set_name(subscribe, STANZA_NAME_SUBSCRIBE); - xmpp_stanza_set_attribute(subscribe, "node", STANZA_NS_OMEMO_DEVICELIST); + xmpp_stanza_set_attribute(subscribe, STANZA_ATTR_NODE, STANZA_NS_OMEMO_DEVICELIST); xmpp_stanza_set_attribute(subscribe, "jid", jid); xmpp_stanza_add_child(pubsub, subscribe); @@ -2205,7 +2205,7 @@ stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids) xmpp_stanza_t *publish = xmpp_stanza_new(ctx); xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); - xmpp_stanza_set_attribute(publish, "node", STANZA_NS_OMEMO_DEVICELIST); + xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, STANZA_NS_OMEMO_DEVICELIST); xmpp_stanza_t *item = xmpp_stanza_new(ctx); xmpp_stanza_set_name(item, STANZA_NAME_ITEM); @@ -2241,15 +2241,14 @@ stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids) } xmpp_stanza_t* -stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, +stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, const char *const id, + uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length) { - char *id = connection_create_stanza_id("omemo_bundle_publish"); xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); - free(id); xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); @@ -2258,7 +2257,7 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, xmpp_stanza_t *publish = xmpp_stanza_new(ctx); xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); char *node = g_strdup_printf("%s:%d", "eu.siacs.conversations.axolotl.bundles", device_id); - xmpp_stanza_set_attribute(publish, "node", node); + xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, node); g_free(node); xmpp_stanza_t *item = xmpp_stanza_new(ctx); @@ -2356,7 +2355,7 @@ stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const xmpp_stanza_t *items = xmpp_stanza_new(ctx); xmpp_stanza_set_name(items, "items"); char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, device_id); - xmpp_stanza_set_attribute(items, "node", node); + xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, node); g_free(node); xmpp_stanza_add_child(pubsub, items); @@ -2368,6 +2367,56 @@ stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const return iq; } +xmpp_stanza_t* +stanza_create_pubsub_configure_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node) +{ + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id); + xmpp_stanza_set_to(iq, jid); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB_OWNER); + + xmpp_stanza_t *configure = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(configure, STANZA_NAME_CONFIGURE); + xmpp_stanza_set_attribute(configure, STANZA_ATTR_NODE, node); + + xmpp_stanza_add_child(pubsub, configure); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(configure); + xmpp_stanza_release(pubsub); + + return iq; +} + +xmpp_stanza_t* +stanza_create_pubsub_configure_submit(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node, DataForm *form) +{ + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + xmpp_stanza_set_to(iq, jid); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB_OWNER); + + xmpp_stanza_t *configure = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(configure, STANZA_NAME_CONFIGURE); + xmpp_stanza_set_attribute(configure, STANZA_ATTR_NODE, node); + + xmpp_stanza_t *x = form_create_submission(form); + + xmpp_stanza_add_child(configure, x); + xmpp_stanza_add_child(pubsub, configure); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(x); + xmpp_stanza_release(configure); + xmpp_stanza_release(pubsub); + + return iq; +} + static void _stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 2efb4cff..eaf76a27 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -101,6 +101,7 @@ #define STANZA_NAME_GET "get" #define STANZA_NAME_URL "url" #define STANZA_NAME_COMMAND "command" +#define STANZA_NAME_CONFIGURE "configure" // error conditions #define STANZA_NAME_BAD_REQUEST "bad-request" @@ -180,6 +181,7 @@ #define STANZA_NS_CONFERENCE "jabber:x:conference" #define STANZA_NS_CAPTCHA "urn:xmpp:captcha" #define STANZA_NS_PUBSUB "http://jabber.org/protocol/pubsub" +#define STANZA_NS_PUBSUB_OWNER "http://jabber.org/protocol/pubsub#owner" #define STANZA_NS_PUBSUB_EVENT "http://jabber.org/protocol/pubsub#event" #define STANZA_NS_CARBONS "urn:xmpp:carbons:2" #define STANZA_NS_HINTS "urn:xmpp:hints" @@ -295,9 +297,12 @@ void stanza_attach_publish_options(xmpp_ctx_t *const ctx, xmpp_stanza_t *const p xmpp_stanza_t* stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids); -xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length); +xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, const char *const id, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length); xmpp_stanza_t* stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, uint32_t device_id); +xmpp_stanza_t* stanza_create_pubsub_configure_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node); +xmpp_stanza_t* stanza_create_pubsub_configure_submit(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node, DataForm *form); + int stanza_get_idle_time(xmpp_stanza_t *const stanza); void stanza_attach_priority(xmpp_ctx_t *const ctx, xmpp_stanza_t *const presence, const int pri); -- cgit 1.4.1-2-gfad0 From 0d0e2b246ac3ec7a415f7dd50fca77ab802f885f Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Fri, 29 Mar 2019 13:54:21 +0100 Subject: Add support for missing from in bundle iq --- src/xmpp/omemo.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 66b0ebcc..c8ac7ed5 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -107,9 +107,14 @@ omemo_bundle_request(const char * const jid, uint32_t device_id, ProfIqCallback int omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *const userdata) { - const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); - if (!from) { - return 1; + char *from = NULL; + const char *from_attr = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + if (!from_attr) { + Jid *jid = jid_create(connection_get_fulljid()); + from = strdup(jid->barejid); + jid_destroy(jid); + } else { + from = strdup(from_attr); } if (g_strcmp0(from, userdata) != 0) { @@ -219,6 +224,7 @@ omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *cons signed_prekey_raw, signed_prekey_len, signed_prekey_signature_raw, signed_prekey_signature_len, identity_key_raw, identity_key_len); + free(from); g_list_free_full(prekeys_list, (GDestroyNotify)omemo_key_free); g_free(signed_prekey_raw); g_free(identity_key_raw); -- cgit 1.4.1-2-gfad0 From 5cd2b8dc9d7823d479dff33adc2c916ba1b772e0 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 1 Apr 2019 13:00:19 +0200 Subject: Permanently store pre keys --- src/omemo/omemo.c | 96 +++++++++++++++++++++++++++++++++++++++++++------------ src/omemo/store.c | 43 +++++++++++++++++++++++-- src/omemo/store.h | 2 ++ 3 files changed, 119 insertions(+), 22 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 580416f8..0515cbf9 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -27,7 +27,7 @@ static gboolean loaded; -static void omemo_generate_short_term_crypto_materials(ProfAccount *account); +static void omemo_publish_crypto_materials(ProfAccount *account); static void load_identity(void); static void load_sessions(void); static void lock(void *user_data); @@ -210,7 +210,7 @@ omemo_on_connect(ProfAccount *account) if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { load_identity(); - omemo_generate_short_term_crypto_materials(account); + omemo_publish_crypto_materials(account); } else if (error->code != G_FILE_ERROR_NOENT) { log_warning("OMEMO: error loading identity from: %s, %s", omemo_ctx.identity_filename->str, error->message); return; @@ -255,37 +255,32 @@ omemo_generate_crypto_materials(ProfAccount *account) } log_info("Generate long term OMEMO cryptography metarials"); + + /* Device ID */ gcry_randomize(&omemo_ctx.device_id, 4, GCRY_VERY_STRONG_RANDOM); omemo_ctx.device_id &= 0x7fffffff; + g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, omemo_ctx.device_id); log_info("OMEMO: device id: %d", omemo_ctx.device_id); + /* Identity key */ signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); - signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); ec_public_key_serialize(&omemo_ctx.identity_key_store.public, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair)); - ec_private_key_serialize(&omemo_ctx.identity_key_store.private, ratchet_identity_key_pair_get_private(omemo_ctx.identity_key_pair)); - - g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, omemo_ctx.device_id); - g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, omemo_ctx.registration_id); char *identity_key_public = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.public), signal_buffer_len(omemo_ctx.identity_key_store.public)); g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, identity_key_public); g_free(identity_key_public); + + ec_private_key_serialize(&omemo_ctx.identity_key_store.private, ratchet_identity_key_pair_get_private(omemo_ctx.identity_key_pair)); char *identity_key_private = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.private), signal_buffer_len(omemo_ctx.identity_key_store.private)); g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, identity_key_private); g_free(identity_key_private); - omemo_identity_keyfile_save(); - - omemo_generate_short_term_crypto_materials(account); -} + /* Registration ID */ + signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); + g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, omemo_ctx.registration_id); -static void -omemo_generate_short_term_crypto_materials(ProfAccount *account) -{ + /* Pre keys */ unsigned int start; - - log_info("Generate short term OMEMO cryptography metarials"); - gcry_randomize(&start, sizeof(unsigned int), GCRY_VERY_STRONG_RANDOM); signal_protocol_key_helper_pre_key_list_node *pre_keys_head; signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start, 100, omemo_ctx.signal); @@ -297,6 +292,7 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) } signal_protocol_key_helper_key_list_free(pre_keys_head); + /* Signed pre key */ session_signed_pre_key *signed_pre_key; struct timeval tv; gettimeofday(&tv, NULL); @@ -307,8 +303,16 @@ omemo_generate_short_term_crypto_materials(ProfAccount *account) signal_protocol_signed_pre_key_store_key(omemo_ctx.store, signed_pre_key); SIGNAL_UNREF(signed_pre_key); + omemo_identity_keyfile_save(); + loaded = TRUE; + omemo_publish_crypto_materials(account); +} + +static void +omemo_publish_crypto_materials(ProfAccount *account) +{ /* Ensure we get our current device list, and it gets updated with our * device_id */ g_hash_table_insert(omemo_ctx.device_list_handler, strdup(account->jid), handle_own_device_list); @@ -378,7 +382,12 @@ omemo_signed_prekey(unsigned char **output, size_t *length) session_signed_pre_key *signed_pre_key; signal_buffer *buffer = NULL; - signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id); + if (signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id) != SG_SUCCESS) { + *output = NULL; + *length = 0; + return; + } + ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(signed_pre_key))); SIGNAL_UNREF(signed_pre_key); *length = signal_buffer_len(buffer); @@ -392,7 +401,12 @@ omemo_signed_prekey_signature(unsigned char **output, size_t *length) { session_signed_pre_key *signed_pre_key; - signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id); + if (signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id) != SG_SUCCESS) { + *output = NULL; + *length = 0; + return; + } + *length = session_signed_pre_key_get_signature_len(signed_pre_key); *output = malloc(*length); memcpy(*output, session_signed_pre_key_get_signature(signed_pre_key), *length); @@ -1134,10 +1148,15 @@ static void load_identity(void) { log_info("Loading OMEMO identity"); + + /* Device ID */ omemo_ctx.device_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, NULL); log_info("OMEMO: device id: %d", omemo_ctx.device_id); + + /* Registration ID */ omemo_ctx.registration_id = g_key_file_get_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, NULL); + /* Identity key */ char *identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, NULL); size_t identity_key_public_len; unsigned char *identity_key_public = g_base64_decode(identity_key_public_b64, &identity_key_public_len); @@ -1159,7 +1178,42 @@ load_identity(void) g_free(identity_key_public); g_free(identity_key_private); - char **keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, NULL, NULL); + char **keys = NULL; + /* Pre keys */ + keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_PREKEYS, NULL, NULL); + if (keys) { + int i; + for (i = 0; keys[i] != NULL; i++) { + char *pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_PREKEYS, keys[i], NULL); + size_t pre_key_len; + unsigned char *pre_key = g_base64_decode(pre_key_b64, &pre_key_len); + g_free(pre_key_b64); + signal_buffer *buffer = signal_buffer_create(pre_key, pre_key_len); + g_free(pre_key); + g_hash_table_insert(omemo_ctx.pre_key_store, GINT_TO_POINTER(strtoul(keys[i], NULL, 10)), buffer); + } + } + g_strfreev(keys); + + /* Signed pre keys */ + keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_SIGNED_PREKEYS, NULL, NULL); + if (keys) { + int i; + for (i = 0; keys[i] != NULL; i++) { + char *signed_pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_SIGNED_PREKEYS, keys[i], NULL); + size_t signed_pre_key_len; + unsigned char *signed_pre_key = g_base64_decode(signed_pre_key_b64, &signed_pre_key_len); + g_free(signed_pre_key_b64); + signal_buffer *buffer = signal_buffer_create(signed_pre_key, signed_pre_key_len); + g_free(signed_pre_key); + g_hash_table_insert(omemo_ctx.signed_pre_key_store, GINT_TO_POINTER(strtoul(keys[i], NULL, 10)), buffer); + omemo_ctx.signed_pre_key_id = strtoul(keys[i], NULL, 10); + } + } + g_strfreev(keys); + + /* Trusted keys */ + keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, NULL, NULL); if (keys) { int i; for (i = 0; keys[i] != NULL; i++) { @@ -1173,6 +1227,8 @@ load_identity(void) } } g_strfreev(keys); + + loaded = TRUE; } static void diff --git a/src/omemo/store.c b/src/omemo/store.c index 4b20d29f..3520db6d 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -190,6 +190,16 @@ store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, signal_buffer *buffer = signal_buffer_create(record, record_len); g_hash_table_insert(pre_key_store, GINT_TO_POINTER(pre_key_id), buffer); + + /* Long term storage */ + char *pre_key_id_str = g_strdup_printf("%d", pre_key_id); + char *record_b64 = g_base64_encode(record, record_len); + g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_PREKEYS, pre_key_id_str, record_b64); + g_free(pre_key_id_str); + g_free(record_b64); + + omemo_identity_keyfile_save(); + return SG_SUCCESS; } @@ -206,7 +216,16 @@ remove_pre_key(uint32_t pre_key_id, void *user_data) { GHashTable *pre_key_store = (GHashTable *)user_data; - return g_hash_table_remove(pre_key_store, GINT_TO_POINTER(pre_key_id)); + int ret = g_hash_table_remove(pre_key_store, GINT_TO_POINTER(pre_key_id)); + + /* Long term storage */ + char *pre_key_id_str = g_strdup_printf("%d", pre_key_id); + g_key_file_remove_key(omemo_identity_keyfile(), OMEMO_STORE_GROUP_PREKEYS, pre_key_id_str, NULL); + g_free(pre_key_id_str); + + omemo_identity_keyfile_save(); + + return ret; } int @@ -233,6 +252,16 @@ store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, signal_buffer *buffer = signal_buffer_create(record, record_len); g_hash_table_insert(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id), buffer); + + /* Long term storage */ + char *signed_pre_key_id_str = g_strdup_printf("%d", signed_pre_key_id); + char *record_b64 = g_base64_encode(record, record_len); + g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_SIGNED_PREKEYS, signed_pre_key_id_str, record_b64); + g_free(signed_pre_key_id_str); + g_free(record_b64); + + omemo_identity_keyfile_save(); + return SG_SUCCESS; } @@ -249,7 +278,16 @@ remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) { GHashTable *signed_pre_key_store = (GHashTable *)user_data; - return g_hash_table_remove(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); + int ret = g_hash_table_remove(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); + + /* Long term storage */ + char *signed_pre_key_id_str = g_strdup_printf("%d", signed_pre_key_id); + g_key_file_remove_key(omemo_identity_keyfile(), OMEMO_STORE_GROUP_PREKEYS, signed_pre_key_id_str, NULL); + g_free(signed_pre_key_id_str); + + omemo_identity_keyfile_save(); + + return ret; } int @@ -284,6 +322,7 @@ save_identity(const signal_protocol_address *address, uint8_t *key_data, signal_buffer *buffer = signal_buffer_create(key_data, key_len); g_hash_table_insert(identity_key_store->trusted, strdup(node), buffer); + /* Long term storage */ char *key_b64 = g_base64_encode(key_data, key_len); g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_TRUST, node, key_b64); g_free(key_b64); diff --git a/src/omemo/store.h b/src/omemo/store.h index 03800f84..598f1bfa 100644 --- a/src/omemo/store.h +++ b/src/omemo/store.h @@ -3,6 +3,8 @@ #include "config.h" #define OMEMO_STORE_GROUP_IDENTITY "identity" +#define OMEMO_STORE_GROUP_PREKEYS "prekeys" +#define OMEMO_STORE_GROUP_SIGNED_PREKEYS "signed_prekeys" #define OMEMO_STORE_GROUP_TRUST "trust" #define OMEMO_STORE_KEY_DEVICE_ID "device_id" #define OMEMO_STORE_KEY_REGISTRATION_ID "registration_id" -- cgit 1.4.1-2-gfad0 From 898302243e2890c0d2ffc6c48451718e43a85d23 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 1 Apr 2019 13:14:46 +0200 Subject: Add clear_device_list command --- src/command/cmd_ac.c | 1 + src/command/cmd_defs.c | 11 +++++++---- src/command/cmd_funcs.c | 19 +++++++++++++++++++ src/command/cmd_funcs.h | 1 + 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index c700e51b..7b6a865b 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -589,6 +589,7 @@ cmd_ac_init(void) autocomplete_add(omemo_ac, "trust"); autocomplete_add(omemo_ac, "untrust"); autocomplete_add(omemo_ac, "fingerprint"); + autocomplete_add(omemo_ac, "clear_device_list"); omemo_log_ac = autocomplete_new(); autocomplete_add(omemo_log_ac, "on"); diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 5a3e6873..b1270f63 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2339,7 +2339,8 @@ static struct cmd_t command_defs[] = { "end", cmd_omemo_end }, { "trust", cmd_omemo_trust }, { "untrust", cmd_omemo_untrust }, - { "fingerprint", cmd_omemo_fingerprint }) + { "fingerprint", cmd_omemo_fingerprint }, + { "clear_device_list", cmd_omemo_clear_device_list }) CMD_NOMAINFUNC CMD_TAGS( CMD_TAG_CHAT, @@ -2350,7 +2351,8 @@ static struct cmd_t command_defs[] = "/omemo start []", "/omemo trust [] ", "/omemo end", - "/omemo fingerprint []") + "/omemo fingerprint []", + "/omemo clear_device_list") CMD_DESC( "Omemo commands to manage keys, and perform encryption during chat sessions.") CMD_ARGS( @@ -2359,12 +2361,13 @@ static struct cmd_t command_defs[] = { "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]. This is the default." }, - { "fingerprint", "Show contact fingerprints." }) + { "fingerprint", "Show contact fingerprints." }, + { "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."}) CMD_EXAMPLES( "/omemo gen", "/omemo start buddy@buddychat.org", "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", - "/omemo untrust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a") + "/omemo untrust buddy@buddychat.org c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a") }, }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 369a992f..c2696c31 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -87,6 +87,7 @@ #ifdef HAVE_OMEMO #include "omemo/omemo.h" +#include "xmpp/omemo.h" #endif #ifdef HAVE_GTK @@ -8268,3 +8269,21 @@ cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args) return TRUE; #endif } + +gboolean +cmd_omemo_clear_device_list(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to initialize OMEMO."); + return TRUE; + } + + omemo_devicelist_publish(NULL); + cons_show("Cleared OMEMO device list"); + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 3f3385a5..3410075d 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -221,5 +221,6 @@ gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args) gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_clear_device_list(ProfWin *window, const char *const command, gchar **args); #endif -- cgit 1.4.1-2-gfad0 From 65b3f82c5f40e26b384d734a3d1206c09bf45ba5 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 1 Apr 2019 13:42:24 +0200 Subject: Fix devicelist notification handler devicelist handler should be kept after trigger --- src/xmpp/message.c | 3 +-- src/xmpp/omemo.c | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/xmpp/message.c b/src/xmpp/message.c index bc91ef3b..2a6ad234 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -142,7 +142,6 @@ _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *con if (handler) { int keep = handler->func(stanza, handler->userdata); if (!keep) { - free(handler); g_hash_table_remove(pubsub_event_handlers, node); } } @@ -176,7 +175,7 @@ message_handlers_init(void) g_hash_table_destroy(pubsub_event_handlers); } - pubsub_event_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); + pubsub_event_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); } void diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index c8ac7ed5..8c8d56e0 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -337,19 +337,19 @@ _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) } if (!root) { - return 0; + return 1; } xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(root, "items"); if (!items) { - return 0; + return 1; } xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); if (item) { xmpp_stanza_t *list = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO); if (!list) { - return 0; + return 1; } xmpp_stanza_t *device; @@ -360,7 +360,7 @@ _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata) } omemo_set_device_list(from, device_list); - return 0; + return 1; } static int -- cgit 1.4.1-2-gfad0 From e7be3a605bbb47e462265a379d48aad9cc565fc2 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 1 Apr 2019 13:53:29 +0200 Subject: Add OMEMO in prefs command --- src/command/cmd_ac.c | 1 + src/command/cmd_defs.c | 5 +++-- src/command/cmd_funcs.c | 4 ++++ src/ui/console.c | 24 ++++++++++++++++++++++++ src/ui/ui.h | 1 + tests/unittests/ui/stub_ui.c | 1 + 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 7b6a865b..0cc28bb3 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -244,6 +244,7 @@ cmd_ac_init(void) autocomplete_add(prefs_ac, "presence"); autocomplete_add(prefs_ac, "otr"); autocomplete_add(prefs_ac, "pgp"); + autocomplete_add(prefs_ac, "omemo"); notify_ac = autocomplete_new(); autocomplete_add(notify_ac, "chat"); diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index b1270f63..ab4112c7 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2134,7 +2134,7 @@ static struct cmd_t command_defs[] = CMD_MAINFUNC(cmd_prefs) CMD_NOTAGS CMD_SYN( - "/prefs [ui|desktop|chat|log|conn|presence|otr|pgp]") + "/prefs [ui|desktop|chat|log|conn|presence|otr|pgp|omemo]") CMD_DESC( "Show preferences for different areas of functionality. " "Passing no arguments shows all preferences.") @@ -2146,7 +2146,8 @@ static struct cmd_t command_defs[] = { "conn", "Connection handling preferences." }, { "presence", "Chat presence preferences." }, { "otr", "Off The Record preferences." }, - { "pgp", "OpenPGP preferences." }) + { "pgp", "OpenPGP preferences." }, + { "omemo", "OMEMO preferences." }) CMD_NOEXAMPLES }, diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index c2696c31..e1f62ace 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -1679,6 +1679,10 @@ cmd_prefs(ProfWin *window, const char *const command, gchar **args) cons_show(""); cons_show_pgp_prefs(); cons_show(""); + } else if (strcmp(args[0], "omemo") == 0) { + cons_show(""); + cons_show_omemo_prefs(); + cons_show(""); } else { cons_bad_cmd_usage(command); } diff --git a/src/ui/console.c b/src/ui/console.c index e5c12158..260658c8 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -1998,6 +1998,28 @@ cons_show_pgp_prefs(void) cons_alert(); } +void +cons_show_omemo_prefs(void) +{ + cons_show("OMEMO preferences:"); + cons_show(""); + + char *log_value = prefs_get_string(PREF_OMEMO_LOG); + if (strcmp(log_value, "on") == 0) { + cons_show("OMEMO logging (/omemo log) : ON"); + } else if (strcmp(log_value, "off") == 0) { + cons_show("OMEMO logging (/omemo log) : OFF"); + } else { + cons_show("OMEMO logging (/omemo log) : Redacted"); + } + prefs_free_string(log_value); + + char ch = prefs_get_omemo_char(); + cons_show("OMEMO char (/omemo char) : %c", ch); + + cons_alert(); +} + void cons_show_themes(GSList *themes) { @@ -2072,6 +2094,8 @@ cons_prefs(void) cons_show(""); cons_show_pgp_prefs(); cons_show(""); + cons_show_omemo_prefs(); + cons_show(""); cons_alert(); } diff --git a/src/ui/ui.h b/src/ui/ui.h index e3cece81..79701a27 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -252,6 +252,7 @@ void cons_show_presence_prefs(void); void cons_show_connection_prefs(void); void cons_show_otr_prefs(void); void cons_show_pgp_prefs(void); +void cons_show_omemo_prefs(void); void cons_show_account(ProfAccount *account); void cons_debug(const char *const msg, ...); void cons_show_error(const char *const cmd, ...); diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index e08c3ce1..950036bc 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -338,6 +338,7 @@ void cons_show_presence_prefs(void) {} void cons_show_connection_prefs(void) {} void cons_show_otr_prefs(void) {} void cons_show_pgp_prefs(void) {} +void cons_show_omemo_prefs(void) {} void cons_show_account(ProfAccount *account) { -- cgit 1.4.1-2-gfad0 From e69f947547160ea2c965a3d4f5966c5f4a289340 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 1 Apr 2019 20:39:39 +0320 Subject: Rework MUC reflected message filtering Reflected messages can't be filtered by nick only otherwise you might ignore messages comming from you on another devices. Consequently we maintain a list of sent messages id in mucwin. To be sure the id will be correctly reflected we use the origin-id stanza. --- src/event/client_events.c | 12 +++++++----- src/event/server_events.c | 10 +++------- src/event/server_events.h | 2 +- src/ui/mucwin.c | 11 +++++++++-- src/ui/ui.h | 4 ++-- src/ui/win_types.h | 1 + src/ui/window.c | 1 + src/ui/window_list.c | 1 + src/xmpp/message.c | 18 ++++++++++++++---- src/xmpp/stanza.c | 15 +++++++++++++++ src/xmpp/stanza.h | 3 +++ src/xmpp/xmpp.h | 2 +- tests/unittests/ui/stub_ui.c | 4 ++-- tests/unittests/xmpp/stub_xmpp.c | 5 ++++- 14 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/event/client_events.c b/src/event/client_events.c index adcffbf2..c2149985 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -353,12 +353,13 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const if (mucwin->is_omemo) { char *id = omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE); groupchat_log_omemo_msg_out(mucwin->roomjid, plugin_msg); - mucwin_outgoing_msg(mucwin, plugin_msg, PROF_MSG_OMEMO); + mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_OMEMO); free(id); } else { - message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); groupchat_log_msg_out(mucwin->roomjid, plugin_msg); - mucwin_outgoing_msg(mucwin, plugin_msg, PROF_MSG_PLAIN); + mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN); + free(id); } plugins_post_room_message_send(mucwin->roomjid, plugin_msg); @@ -367,9 +368,10 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const #endif #ifndef HAVE_OMEMO - message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); + char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url); groupchat_log_msg_out(mucwin->roomjid, plugin_msg); - mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt); + mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN); + free(id); plugins_post_room_message_send(mucwin->roomjid, plugin_msg); free(plugin_msg); diff --git a/src/event/server_events.c b/src/event/server_events.c index 023e8a13..f6c640f3 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -260,7 +260,7 @@ sv_ev_room_history(const char *const room_jid, const char *const nick, } void -sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message, gboolean omemo) +sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message, const char *const id, gboolean omemo) { ProfMucWin *mucwin = wins_get_muc(room_jid); if (!mucwin) { @@ -268,10 +268,6 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha } char *mynick = muc_nick(mucwin->roomjid); - if (g_strcmp0(mynick, nick) == 0) { - /* Ignore message reflection */ - return; - } if (omemo) { groupchat_log_omemo_msg_in(room_jid, nick, message); @@ -295,9 +291,9 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha GList *triggers = prefs_message_get_triggers(new_message); if (omemo) { - mucwin_incoming_msg(mucwin, nick, new_message, mentions, triggers, PROF_MSG_OMEMO); + mucwin_incoming_msg(mucwin, nick, new_message, id, mentions, triggers, PROF_MSG_OMEMO); } else { - mucwin_incoming_msg(mucwin, nick, new_message, mentions, triggers, PROF_MSG_PLAIN); + mucwin_incoming_msg(mucwin, nick, new_message, id, mentions, triggers, PROF_MSG_PLAIN); } g_slist_free(mentions); diff --git a/src/event/server_events.h b/src/event/server_events.h index cf437f9a..713e50f5 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -48,7 +48,7 @@ void sv_ev_room_subject(const char *const room, const char *const nick, const ch void sv_ev_room_history(const char *const room_jid, const char *const nick, GDateTime *timestamp, const char *const message, gboolean omemo); void sv_ev_room_message(const char *const room_jid, const char *const nick, - const char *const message, gboolean omemo); + const char *const message, const char *const id, gboolean omemo); void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo); void sv_ev_incoming_private_message(const char *const fulljid, char *message); void sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTime *timestamp); diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c index abbcd21c..0122950a 100644 --- a/src/ui/mucwin.c +++ b/src/ui/mucwin.c @@ -478,10 +478,12 @@ _mucwin_print_triggers(ProfWin *window, const char *const message, GList *trigge } void -mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, prof_enc_t enc_mode) +mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode) { assert(mucwin != NULL); + g_hash_table_insert(mucwin->sent_messages, strdup(id), NULL); + ProfWin *window = (ProfWin*)mucwin; char *mynick = muc_nick(mucwin->roomjid); @@ -500,10 +502,15 @@ mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, prof_enc_t en } void -mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers, prof_enc_t enc_mode) +mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode) { assert(mucwin != NULL); + if (g_hash_table_remove(mucwin->sent_messages, id)) { + /* Ignore reflection messages */ + return; + } + ProfWin *window = (ProfWin*)mucwin; char *mynick = muc_nick(mucwin->roomjid); diff --git a/src/ui/ui.h b/src/ui/ui.h index 79701a27..b94fe475 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -162,8 +162,8 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char const char *const role, const char *const affiliation, const char *const actor, const char *const reason); void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char *const presence); void mucwin_history(ProfMucWin *mucwin, const char *const nick, GDateTime *timestamp, const char *const message); -void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, prof_enc_t enc_mode); -void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers, prof_enc_t enc_mode); +void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode); +void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode); void mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const subject); void mucwin_requires_config(ProfMucWin *mucwin); void mucwin_info(ProfMucWin *mucwin); diff --git a/src/ui/win_types.h b/src/ui/win_types.h index 498d9bbe..e1e64bf9 100644 --- a/src/ui/win_types.h +++ b/src/ui/win_types.h @@ -172,6 +172,7 @@ typedef struct prof_muc_win_t { unsigned long memcheck; char *enctext; char *message_char; + GHashTable *sent_messages; } ProfMucWin; typedef struct prof_conf_win_t ProfConfWin; diff --git a/src/ui/window.c b/src/ui/window.c index 64b04365..12b6c15b 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -198,6 +198,7 @@ win_create_muc(const char *const roomjid) new_win->enctext = NULL; new_win->message_char = NULL; new_win->is_omemo = FALSE; + new_win->sent_messages = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); new_win->memcheck = PROFMUCWIN_MEMCHECK; diff --git a/src/ui/window_list.c b/src/ui/window_list.c index 5ce68d63..43230b57 100644 --- a/src/ui/window_list.c +++ b/src/ui/window_list.c @@ -561,6 +561,7 @@ wins_close_by_num(int i) ProfMucWin *mucwin = (ProfMucWin*)window; autocomplete_remove(wins_ac, mucwin->roomjid); autocomplete_remove(wins_close_ac, mucwin->roomjid); + g_hash_table_remove_all(mucwin->sent_messages); break; } case WIN_PRIVATE: diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 2a6ad234..47cf35d7 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -326,6 +326,7 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, if (muc) { id = connection_create_stanza_id("muc"); message = xmpp_message_new(ctx, STANZA_TYPE_GROUPCHAT, jid, id); + stanza_attach_origin_id(ctx, message, id); } else { id = connection_create_stanza_id("msg"); message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id); @@ -442,23 +443,24 @@ message_send_private(const char *const fulljid, const char *const msg, const cha xmpp_stanza_release(message); } -void +char* message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url) { xmpp_ctx_t * const ctx = connection_get_ctx(); char *id = connection_create_stanza_id("muc"); xmpp_stanza_t *message = xmpp_message_new(ctx, STANZA_TYPE_GROUPCHAT, roomjid, id); + stanza_attach_origin_id(ctx, message, id); xmpp_message_set_body(message, msg); - free(id); - if (oob_url) { stanza_attach_x_oob_url(ctx, message, oob_url); } _send_message_stanza(message); xmpp_stanza_release(message); + + return id; } void @@ -687,6 +689,14 @@ _handle_groupchat(xmpp_stanza_t *const stanza) { xmpp_ctx_t *ctx = connection_get_ctx(); char *message = NULL; + + const char *id = xmpp_stanza_get_id(stanza); + + xmpp_stanza_t *origin = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_STABLE_ID); + if (origin && g_strcmp0(xmpp_stanza_get_name(origin), STANZA_NAME_ORIGIN_ID) == 0) { + id = xmpp_stanza_get_attribute(origin, STANZA_ATTR_ID); + } + const char *room_jid = xmpp_stanza_get_from(stanza); Jid *jid = jid_create(room_jid); @@ -750,7 +760,7 @@ _handle_groupchat(xmpp_stanza_t *const stanza) sv_ev_room_history(jid->barejid, jid->resourcepart, timestamp, message, omemo); g_date_time_unref(timestamp); } else { - sv_ev_room_message(jid->barejid, jid->resourcepart, message, omemo); + sv_ev_room_message(jid->barejid, jid->resourcepart, message, id, omemo); } xmpp_free(ctx, message); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index f4fd8aa8..615de44f 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2417,6 +2417,21 @@ stanza_create_pubsub_configure_submit(xmpp_ctx_t *ctx, const char *const id, con return iq; } +xmpp_stanza_t* +stanza_attach_origin_id(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const id) +{ + xmpp_stanza_t *origin_id = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(origin_id, STANZA_NAME_ORIGIN_ID); + xmpp_stanza_set_ns(origin_id, STANZA_NS_STABLE_ID); + xmpp_stanza_set_attribute(origin_id, STANZA_ATTR_ID, id); + + xmpp_stanza_add_child(stanza, origin_id); + + xmpp_stanza_release(origin_id); + + return stanza; +} + static void _stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index eaf76a27..e5e17ba4 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -102,6 +102,7 @@ #define STANZA_NAME_URL "url" #define STANZA_NAME_COMMAND "command" #define STANZA_NAME_CONFIGURE "configure" +#define STANZA_NAME_ORIGIN_ID "origin-id" // error conditions #define STANZA_NAME_BAD_REQUEST "bad-request" @@ -196,6 +197,7 @@ #define STANZA_NS_OMEMO "eu.siacs.conversations.axolotl" #define STANZA_NS_OMEMO_DEVICELIST "eu.siacs.conversations.axolotl.devicelist" #define STANZA_NS_OMEMO_BUNDLES "eu.siacs.conversations.axolotl.bundles" +#define STANZA_NS_STABLE_ID "urn:xmpp:sid:0" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" @@ -238,6 +240,7 @@ xmpp_stanza_t* stanza_attach_hints_no_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stan xmpp_stanza_t* stanza_attach_hints_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); xmpp_stanza_t* stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza); xmpp_stanza_t* stanza_attach_x_oob_url(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const url); +xmpp_stanza_t* stanza_attach_origin_id(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const id); xmpp_stanza_t* stanza_create_room_join_presence(xmpp_ctx_t *const ctx, const char *const full_room_jid, const char *const passwd); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 39877bc2..54a56f02 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -144,7 +144,7 @@ char* message_send_chat_otr(const char *const barejid, const char *const msg, gb char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt); char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt, gboolean muc); void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url); -void message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url); +char* message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url); void message_send_groupchat_subject(const char *const roomjid, const char *const subject); void message_send_inactive(const char *const jid); void message_send_composing(const char *const jid); diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 950036bc..361a42e2 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -190,8 +190,8 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char const char * const affiliation, const char * const actor, const char * const reason) {} void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char * const presence) {} void mucwin_history(ProfMucWin *mucwin, const char * const nick, GDateTime *timestamp, const char * const message) {} -void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers, prof_enc_t enc_mode) {} -void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, prof_enc_t enc_mode) {} +void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode) {} +void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode) {} void mucwin_subject(ProfMucWin *mucwin, const char * const nick, const char * const subject) {} void mucwin_requires_config(ProfMucWin *mucwin) {} void ui_room_destroy(const char * const roomjid) {} diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index bc2c50db..53a729e6 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -114,7 +114,10 @@ char* message_send_chat_pgp(const char * const barejid, const char * const msg, } void message_send_private(const char * const fulljid, const char * const msg, const char *const oob_url) {} -void message_send_groupchat(const char * const roomjid, const char * const msg, const char *const oob_url) {} +char* message_send_groupchat(const char * const roomjid, const char * const msg, const char *const oob_url) +{ + return NULL; +} void message_send_groupchat_subject(const char * const roomjid, const char * const subject) {} void message_send_inactive(const char * const barejid) {} -- cgit 1.4.1-2-gfad0 From 0f4dd61776222202f614d88a0b3d565d88834f77 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 1 Apr 2019 21:12:00 +0200 Subject: Ensure pre keys are generated if missing from long term storage --- src/omemo/omemo.c | 75 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 0515cbf9..2a7a50d8 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -28,6 +28,8 @@ static gboolean loaded; static void omemo_publish_crypto_materials(ProfAccount *account); +static void _generate_pre_keys(int count); +static void _generate_signed_pre_key(void); static void load_identity(void); static void load_sessions(void); static void lock(void *user_data); @@ -280,28 +282,10 @@ omemo_generate_crypto_materials(ProfAccount *account) g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, omemo_ctx.registration_id); /* Pre keys */ - unsigned int start; - gcry_randomize(&start, sizeof(unsigned int), GCRY_VERY_STRONG_RANDOM); - signal_protocol_key_helper_pre_key_list_node *pre_keys_head; - signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start, 100, omemo_ctx.signal); - - signal_protocol_key_helper_pre_key_list_node *p; - for (p = pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { - session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); - signal_protocol_pre_key_store_key(omemo_ctx.store, prekey); - } - signal_protocol_key_helper_key_list_free(pre_keys_head); + _generate_pre_keys(100); /* Signed pre key */ - session_signed_pre_key *signed_pre_key; - struct timeval tv; - gettimeofday(&tv, NULL); - unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; - - omemo_ctx.signed_pre_key_id = 1; - signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, omemo_ctx.identity_key_pair, omemo_ctx.signed_pre_key_id, timestamp, omemo_ctx.signal); - signal_protocol_signed_pre_key_store_key(omemo_ctx.store, signed_pre_key); - SIGNAL_UNREF(signed_pre_key); + _generate_signed_pre_key(); omemo_identity_keyfile_save(); @@ -1179,10 +1163,11 @@ load_identity(void) g_free(identity_key_private); char **keys = NULL; + int i; /* Pre keys */ + i = 0; keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_PREKEYS, NULL, NULL); if (keys) { - int i; for (i = 0; keys[i] != NULL; i++) { char *pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_PREKEYS, keys[i], NULL); size_t pre_key_len; @@ -1192,13 +1177,19 @@ load_identity(void) g_free(pre_key); g_hash_table_insert(omemo_ctx.pre_key_store, GINT_TO_POINTER(strtoul(keys[i], NULL, 10)), buffer); } + + g_strfreev(keys); + } + + /* Ensure we have at least 100 pre keys */ + if (i < 100) { + _generate_pre_keys(100 - i); } - g_strfreev(keys); /* Signed pre keys */ + i = 0; keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_SIGNED_PREKEYS, NULL, NULL); if (keys) { - int i; for (i = 0; keys[i] != NULL; i++) { char *signed_pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_SIGNED_PREKEYS, keys[i], NULL); size_t signed_pre_key_len; @@ -1209,8 +1200,12 @@ load_identity(void) g_hash_table_insert(omemo_ctx.signed_pre_key_store, GINT_TO_POINTER(strtoul(keys[i], NULL, 10)), buffer); omemo_ctx.signed_pre_key_id = strtoul(keys[i], NULL, 10); } + g_strfreev(keys); + } + + if (i == 0) { + _generate_signed_pre_key(); } - g_strfreev(keys); /* Trusted keys */ keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, NULL, NULL); @@ -1229,6 +1224,8 @@ load_identity(void) g_strfreev(keys); loaded = TRUE; + + omemo_identity_keyfile_save(); } static void @@ -1289,3 +1286,33 @@ g_hash_table_free(GHashTable *hash_table) g_hash_table_remove_all(hash_table); g_hash_table_unref(hash_table); } + +static void +_generate_pre_keys(int count) +{ + unsigned int start; + gcry_randomize(&start, sizeof(unsigned int), GCRY_VERY_STRONG_RANDOM); + signal_protocol_key_helper_pre_key_list_node *pre_keys_head; + signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start, count, omemo_ctx.signal); + + signal_protocol_key_helper_pre_key_list_node *p; + for (p = pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { + session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); + signal_protocol_pre_key_store_key(omemo_ctx.store, prekey); + } + signal_protocol_key_helper_key_list_free(pre_keys_head); +} + +static void +_generate_signed_pre_key(void) +{ + session_signed_pre_key *signed_pre_key; + struct timeval tv; + gettimeofday(&tv, NULL); + unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; + + omemo_ctx.signed_pre_key_id = 1; + signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, omemo_ctx.identity_key_pair, omemo_ctx.signed_pre_key_id, timestamp, omemo_ctx.signal); + signal_protocol_signed_pre_key_store_key(omemo_ctx.store, signed_pre_key); + SIGNAL_UNREF(signed_pre_key); +} -- cgit 1.4.1-2-gfad0 From 01e96769c2eb796cef3a9d2413453304c91111e6 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 1 Apr 2019 21:16:39 +0200 Subject: Prefix static function with _ --- src/omemo/omemo.c | 82 +++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 2a7a50d8..d580ed51 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -27,20 +27,20 @@ static gboolean loaded; -static void omemo_publish_crypto_materials(ProfAccount *account); +static void _omemo_publish_crypto_materials(ProfAccount *account); static void _generate_pre_keys(int count); static void _generate_signed_pre_key(void); -static void load_identity(void); -static void load_sessions(void); -static void lock(void *user_data); -static void unlock(void *user_data); -static void omemo_log(int level, const char *message, size_t len, void *user_data); -static gboolean handle_own_device_list(const char *const jid, GList *device_list); -static gboolean handle_device_list_start_session(const char *const jid, GList *device_list); -static char * omemo_fingerprint(ec_public_key *identity, gboolean formatted); -static unsigned char *omemo_fingerprint_decode(const char *const fingerprint, size_t *len); -static void cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity); -static void g_hash_table_free(GHashTable *hash_table); +static void _load_identity(void); +static void _load_sessions(void); +static void _lock(void *user_data); +static void _unlock(void *user_data); +static void _omemo_log(int level, const char *message, size_t len, void *user_data); +static gboolean _handle_own_device_list(const char *const jid, GList *device_list); +static gboolean _handle_device_list_start_session(const char *const jid, GList *device_list); +static char * _omemo_fingerprint(ec_public_key *identity, gboolean formatted); +static unsigned char *_omemo_fingerprint_decode(const char *const fingerprint, size_t *len); +static void _cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity); +static void _g_hash_table_free(GHashTable *hash_table); typedef gboolean (*OmemoDeviceListHandler)(const char *const jid, GList *device_list); @@ -95,7 +95,7 @@ omemo_on_connect(ProfAccount *account) return; } - if (signal_context_set_log_function(omemo_ctx.signal, omemo_log) != 0) { + if (signal_context_set_log_function(omemo_ctx.signal, _omemo_log) != 0) { cons_show("Error initializing OMEMO log"); } @@ -119,7 +119,7 @@ omemo_on_connect(ProfAccount *account) return; } - signal_context_set_locking_functions(omemo_ctx.signal, lock, unlock); + signal_context_set_locking_functions(omemo_ctx.signal, _lock, _unlock); signal_protocol_store_context_create(&omemo_ctx.store, omemo_ctx.signal); @@ -173,7 +173,7 @@ omemo_on_connect(ProfAccount *account) loaded = 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_free); + omemo_ctx.known_devices = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_g_hash_table_free); omemo_ctx.fingerprint_ac = autocomplete_new(); @@ -211,8 +211,8 @@ omemo_on_connect(ProfAccount *account) omemo_ctx.sessions_keyfile = g_key_file_new(); if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { - load_identity(); - omemo_publish_crypto_materials(account); + _load_identity(); + _omemo_publish_crypto_materials(account); } else if (error->code != G_FILE_ERROR_NOENT) { log_warning("OMEMO: error loading identity from: %s, %s", omemo_ctx.identity_filename->str, error->message); return; @@ -220,7 +220,7 @@ omemo_on_connect(ProfAccount *account) error = NULL; if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { - load_sessions(); + _load_sessions(); } else if (error->code != G_FILE_ERROR_NOENT) { log_warning("OMEMO: error loading sessions from: %s, %s", omemo_ctx.sessions_filename->str, error->message); } @@ -231,7 +231,7 @@ void omemo_on_disconnect(void) { signal_protocol_signed_pre_key_remove_key(omemo_ctx.store, omemo_ctx.signed_pre_key_id); - g_hash_table_free(omemo_ctx.signed_pre_key_store); + _g_hash_table_free(omemo_ctx.signed_pre_key_store); GHashTableIter iter; gpointer id; @@ -241,7 +241,7 @@ omemo_on_disconnect(void) signal_protocol_pre_key_remove_key(omemo_ctx.store, GPOINTER_TO_INT(id)); } - g_hash_table_free(omemo_ctx.pre_key_store); + _g_hash_table_free(omemo_ctx.pre_key_store); g_string_free(omemo_ctx.identity_filename, TRUE); g_key_file_free(omemo_ctx.identity_keyfile); @@ -291,15 +291,15 @@ omemo_generate_crypto_materials(ProfAccount *account) loaded = TRUE; - omemo_publish_crypto_materials(account); + _omemo_publish_crypto_materials(account); } static void -omemo_publish_crypto_materials(ProfAccount *account) +_omemo_publish_crypto_materials(ProfAccount *account) { /* Ensure we get our current device list, and it gets updated with our * device_id */ - g_hash_table_insert(omemo_ctx.device_list_handler, strdup(account->jid), handle_own_device_list); + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(account->jid), _handle_own_device_list); omemo_devicelist_request(account->jid); omemo_bundle_publish(true); @@ -313,7 +313,7 @@ omemo_start_session(const char *const barejid) if (!device_list) { log_info("OMEMO: missing device list for %s", barejid); omemo_devicelist_request(barejid); - g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), handle_device_list_start_session); + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), _handle_device_list_start_session); return; } @@ -496,7 +496,7 @@ omemo_start_device_session(const char *const jid, uint32_t device_id, ec_public_key *identity_key; curve_decode_point(&identity_key, identity_key_raw, identity_key_len, omemo_ctx.signal); - cache_device_identity(jid, device_id, identity_key); + _cache_device_identity(jid, device_id, identity_key); gboolean trusted = is_trusted_identity(&address, (uint8_t *)identity_key_raw, identity_key_len, &omemo_ctx.identity_key_store); @@ -858,7 +858,7 @@ char * omemo_own_fingerprint(gboolean formatted) { ec_public_key *identity = ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair); - return omemo_fingerprint(identity, formatted); + return _omemo_fingerprint(identity, formatted); } GList * @@ -892,7 +892,7 @@ omemo_is_trusted_identity(const char *const jid, const char *const fingerprint) }; size_t fingerprint_len; - unsigned char *fingerprint_raw = omemo_fingerprint_decode(fingerprint, &fingerprint_len); + unsigned char *fingerprint_raw = _omemo_fingerprint_decode(fingerprint, &fingerprint_len); unsigned char djb_type[] = {'\x05'}; signal_buffer *buffer = signal_buffer_create(djb_type, 1); buffer = signal_buffer_append(buffer, fingerprint_raw, fingerprint_len); @@ -906,7 +906,7 @@ omemo_is_trusted_identity(const char *const jid, const char *const fingerprint) } static char * -omemo_fingerprint(ec_public_key *identity, gboolean formatted) +_omemo_fingerprint(ec_public_key *identity, gboolean formatted) { int i; signal_buffer *identity_public_key; @@ -948,7 +948,7 @@ omemo_fingerprint(ec_public_key *identity, gboolean formatted) } static unsigned char * -omemo_fingerprint_decode(const char *const fingerprint, size_t *len) +_omemo_fingerprint_decode(const char *const fingerprint, size_t *len) { unsigned char *output = malloc(strlen(fingerprint) / 2 + 1); @@ -1012,7 +1012,7 @@ omemo_trust(const char *const jid, const char *const fingerprint_formatted) .name_len = strlen(jid), .device_id = device_id, }; - unsigned char *fingerprint_raw = omemo_fingerprint_decode(fingerprint_formatted, &len); + unsigned char *fingerprint_raw = _omemo_fingerprint_decode(fingerprint_formatted, &len); unsigned char djb_type[] = {'\x05'}; signal_buffer *buffer = signal_buffer_create(djb_type, 1); buffer = signal_buffer_append(buffer, fingerprint_raw, len); @@ -1027,7 +1027,7 @@ void omemo_untrust(const char *const jid, const char *const fingerprint_formatted) { size_t len; - unsigned char *fingerprint = omemo_fingerprint_decode(fingerprint_formatted, &len); + unsigned char *fingerprint = _omemo_fingerprint_decode(fingerprint_formatted, &len); GHashTableIter iter; gpointer key, value; @@ -1046,21 +1046,21 @@ omemo_untrust(const char *const jid, const char *const fingerprint_formatted) } static void -lock(void *user_data) +_lock(void *user_data) { omemo_context *ctx = (omemo_context *)user_data; pthread_mutex_lock(&ctx->lock); } static void -unlock(void *user_data) +_unlock(void *user_data) { omemo_context *ctx = (omemo_context *)user_data; pthread_mutex_unlock(&ctx->lock); } static void -omemo_log(int level, const char *message, size_t len, void *user_data) +_omemo_log(int level, const char *message, size_t len, void *user_data) { switch (level) { case SG_LOG_ERROR: @@ -1080,7 +1080,7 @@ omemo_log(int level, const char *message, size_t len, void *user_data) } static gboolean -handle_own_device_list(const char *const jid, GList *device_list) +_handle_own_device_list(const char *const jid, GList *device_list) { if (!g_list_find(device_list, GINT_TO_POINTER(omemo_ctx.device_id))) { device_list = g_list_copy(device_list); @@ -1098,7 +1098,7 @@ handle_own_device_list(const char *const jid, GList *device_list) } static gboolean -handle_device_list_start_session(const char *const jid, GList *device_list) +_handle_device_list_start_session(const char *const jid, GList *device_list) { omemo_start_session(jid); @@ -1129,7 +1129,7 @@ omemo_fingerprint_autocomplete_reset(void) } static void -load_identity(void) +_load_identity(void) { log_info("Loading OMEMO identity"); @@ -1229,7 +1229,7 @@ load_identity(void) } static void -load_sessions(void) +_load_sessions(void) { int i; char **groups = g_key_file_get_groups(omemo_ctx.sessions_keyfile, NULL); @@ -1262,7 +1262,7 @@ load_sessions(void) } static void -cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity) +_cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity) { GHashTable *known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid); if (!known_identities) { @@ -1270,7 +1270,7 @@ cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key * g_hash_table_insert(omemo_ctx.known_devices, strdup(jid), known_identities); } - char *fingerprint = omemo_fingerprint(identity, FALSE); + char *fingerprint = _omemo_fingerprint(identity, FALSE); log_info("OMEMO: cache identity for %s:%d: %s", jid, device_id, fingerprint); g_hash_table_insert(known_identities, strdup(fingerprint), GINT_TO_POINTER(device_id)); @@ -1281,7 +1281,7 @@ cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key * } static void -g_hash_table_free(GHashTable *hash_table) +_g_hash_table_free(GHashTable *hash_table) { g_hash_table_remove_all(hash_table); g_hash_table_unref(hash_table); -- cgit 1.4.1-2-gfad0 From edbc15fa2b36d9e131707d7d58b79caec917e7f5 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 10 Apr 2019 17:22:50 +0200 Subject: Don't build OMEMO by default --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0c4d005c..ea1e9f14 100644 --- a/configure.ac +++ b/configure.ac @@ -266,8 +266,9 @@ if test "x$enable_otr" != xno; then [AC_MSG_NOTICE([libotr not found, otr encryption support not enabled])])]) fi -AM_CONDITIONAL([BUILD_OMEMO], [true]) +AM_CONDITIONAL([BUILD_OMEMO], [false]) if test "x$enable_omemo" != xno; then + AM_CONDITIONAL([BUILD_OMEMO], [true]) PKG_CHECK_MODULES([libsignal], [libsignal-protocol-c >= 2.3.2], [LIBS="-lsignal-protocol-c $LIBS"], [AC_MSG_NOTICE([libsignal >= 2.3.2 not found, checking for libsignal 2.3.x...]) -- cgit 1.4.1-2-gfad0 From 4ad6904216fa5917f202a0f1bfe3d4712559aeae Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 8 Apr 2019 20:29:25 +0320 Subject: Wait for discovery end to publish omemo devicelist and bundle Add sv_ev_connection_features_received for that purpose --- src/event/server_events.c | 8 ++++++++ src/event/server_events.h | 1 + src/omemo/omemo.c | 21 ++++++++++++++------- src/omemo/omemo.h | 1 + src/xmpp/connection.c | 23 +++++++++++++++++++++++ src/xmpp/connection.h | 2 ++ src/xmpp/iq.c | 2 ++ src/xmpp/omemo.c | 8 ++++++-- src/xmpp/session.c | 2 +- tests/unittests/omemo/stub_omemo.c | 1 + 10 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index f6c640f3..7c1cfd78 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -176,6 +176,14 @@ sv_ev_roster_received(void) plugins_on_connect(account_name, fulljid); } +void +sv_ev_connection_features_received(void) +{ +#ifdef HAVE_OMEMO + omemo_publish_crypto_materials(); +#endif +} + void sv_ev_lost_connection(void) { diff --git a/src/event/server_events.h b/src/event/server_events.h index 713e50f5..74016ceb 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -85,6 +85,7 @@ void sv_ev_muc_occupant_online(const char *const room, const char *const nick, c void sv_ev_roster_update(const char *const barejid, const char *const name, GSList *groups, const char *const subscription, gboolean pending_out); void sv_ev_roster_received(void); +void sv_ev_connection_features_received(void); int sv_ev_certfail(const char *const errormsg, TLSCertificate *cert); void sv_ev_lastactivity_response(const char *const from, const int seconds, const char *const msg); void sv_ev_bookmark_autojoin(Bookmark *bookmark); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index d580ed51..a20dc109 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -27,7 +27,6 @@ static gboolean loaded; -static void _omemo_publish_crypto_materials(ProfAccount *account); static void _generate_pre_keys(int count); static void _generate_signed_pre_key(void); static void _load_identity(void); @@ -212,7 +211,6 @@ omemo_on_connect(ProfAccount *account) if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { _load_identity(); - _omemo_publish_crypto_materials(account); } else if (error->code != G_FILE_ERROR_NOENT) { log_warning("OMEMO: error loading identity from: %s, %s", omemo_ctx.identity_filename->str, error->message); return; @@ -291,18 +289,27 @@ omemo_generate_crypto_materials(ProfAccount *account) loaded = TRUE; - _omemo_publish_crypto_materials(account); + omemo_publish_crypto_materials(); } -static void -_omemo_publish_crypto_materials(ProfAccount *account) +void +omemo_publish_crypto_materials(void) { + if (loaded != TRUE) { + log_error("OMEMO: cannot publish crypto materials before they are generated"); + return; + } + + Jid *jid = jid_create(connection_get_fulljid()); + /* Ensure we get our current device list, and it gets updated with our * device_id */ - g_hash_table_insert(omemo_ctx.device_list_handler, strdup(account->jid), _handle_own_device_list); - omemo_devicelist_request(account->jid); + g_hash_table_insert(omemo_ctx.device_list_handler, strdup(jid->barejid), _handle_own_device_list); + omemo_devicelist_request(jid->barejid); omemo_bundle_publish(true); + + jid_destroy(jid); } void diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index bbd59b77..72e27429 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -21,6 +21,7 @@ void omemo_on_connect(ProfAccount *account); void omemo_on_disconnect(void); void omemo_generate_crypto_materials(ProfAccount *account); void omemo_key_free(omemo_key_t *key); +void omemo_publish_crypto_materials(void); uint32_t omemo_device_id(void); void omemo_identity_key(unsigned char **output, size_t *length); diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index 2adda46e..afcd8199 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -63,6 +63,7 @@ typedef struct prof_conn_t { char *domain; GHashTable *available_resources; GHashTable *features_by_jid; + GHashTable *requested_features; } ProfConnection; static ProfConnection conn; @@ -89,6 +90,7 @@ connection_init(void) conn.domain = NULL; conn.features_by_jid = NULL; conn.available_resources = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)resource_destroy); + conn.requested_features = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); } void @@ -231,6 +233,10 @@ connection_clear_data(void) if (conn.available_resources) { g_hash_table_remove_all(conn.available_resources); } + + if (conn.requested_features) { + g_hash_table_remove_all(conn.requested_features); + } } #ifdef HAVE_LIBMESODE @@ -313,12 +319,21 @@ connection_jid_for_feature(const char *const feature) return NULL; } +void +connection_request_features(void) +{ + /* We don't record it as a requested feature to avoid triggering th + * sv_ev_connection_features_received too soon */ + iq_disco_info_request_onconnect(conn.domain); +} + void connection_set_disco_items(GSList *items) { GSList *curr = items; while (curr) { DiscoItem *item = curr->data; + g_hash_table_insert(conn.requested_features, strdup(item->jid), NULL); g_hash_table_insert(conn.features_by_jid, strdup(item->jid), g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL)); @@ -357,6 +372,14 @@ connection_get_fulljid(void) } } +void +connection_features_received(const char *const jid) +{ + if (g_hash_table_remove(conn.requested_features, jid) && g_hash_table_size(conn.requested_features) == 0) { + sv_ev_connection_features_received(); + } +} + GHashTable* connection_get_features(const char *const jid) { diff --git a/src/xmpp/connection.h b/src/xmpp/connection.h index 170bc2bf..044cf368 100644 --- a/src/xmpp/connection.h +++ b/src/xmpp/connection.h @@ -53,6 +53,8 @@ void connection_set_disco_items(GSList *items); xmpp_conn_t* connection_get_conn(void); xmpp_ctx_t* connection_get_ctx(void); char *connection_get_domain(void); +void connection_request_features(void); +void connection_features_received(const char *const jid); GHashTable* connection_get_features(const char *const jid); void connection_clear_data(void); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index e1c3c281..d6e4c153 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -2291,6 +2291,8 @@ _disco_info_response_id_handler_onconnect(xmpp_stanza_t *const stanza, void *con } } + connection_features_received(from); + return 0; } diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 8c8d56e0..4b77ef23 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -28,7 +28,9 @@ omemo_devicelist_publish(GList *device_list) xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, device_list); - stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); + if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { + stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); + } iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -76,7 +78,9 @@ omemo_bundle_publish(gboolean first) g_list_free(lengths); g_list_free(ids); - stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); + if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) { + stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open"); + } iq_id_handler_add(id, _omemo_bundle_publish_result, NULL, GINT_TO_POINTER(first)); diff --git a/src/xmpp/session.c b/src/xmpp/session.c index 786b1cd5..675f23af 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -320,8 +320,8 @@ session_login_success(gboolean secured) blocking_request(); // items discovery + connection_request_features(); char *domain = connection_get_domain(); - iq_disco_info_request_onconnect(domain); iq_disco_items_request_onconnect(domain); if (prefs_get_boolean(PREF_CARBONS)){ diff --git a/tests/unittests/omemo/stub_omemo.c b/tests/unittests/omemo/stub_omemo.c index 200226b0..84398378 100644 --- a/tests/unittests/omemo/stub_omemo.c +++ b/tests/unittests/omemo/stub_omemo.c @@ -59,3 +59,4 @@ void omemo_start_session(const char *const barejid) {} void omemo_trust(const char *const jid, const char *const fingerprint_formatted) {} void omemo_untrust(const char *const jid, const char *const fingerprint_formatted) {} void omemo_devicelist_publish(GList *device_list) {} +void omemo_publish_crypto_materials(void) {} -- cgit 1.4.1-2-gfad0 From 25eb138d0b1cdddb5b0338f84c5b5f4dca0098ca Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 9 Apr 2019 06:21:02 +0520 Subject: Rework trusted fingerprint storage Stop using "jid:device_id" keys. And move long term storage to its own file: trust.txt. --- src/omemo/omemo.c | 91 +++++++++++++++++++++++++++++++++++++++++++------------ src/omemo/omemo.h | 2 ++ src/omemo/store.c | 25 ++++++++++----- src/omemo/store.h | 1 - 4 files changed, 90 insertions(+), 29 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index a20dc109..377a2637 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -30,6 +30,7 @@ static gboolean loaded; static void _generate_pre_keys(int count); static void _generate_signed_pre_key(void); static void _load_identity(void); +static void _load_trust(void); static void _load_sessions(void); static void _lock(void *user_data); static void _unlock(void *user_data); @@ -61,6 +62,8 @@ struct omemo_context_t { GHashTable *device_ids; GString *identity_filename; GKeyFile *identity_keyfile; + GString *trust_filename; + GKeyFile *trust_keyfile; GString *sessions_filename; GKeyFile *sessions_keyfile; GHashTable *known_devices; @@ -187,6 +190,8 @@ omemo_on_connect(ProfAccount *account) omemo_ctx.identity_filename = g_string_new(basedir->str); g_string_append(omemo_ctx.identity_filename, "identity.txt"); + omemo_ctx.trust_filename = g_string_new(basedir->str); + g_string_append(omemo_ctx.trust_filename, "trust.txt"); omemo_ctx.sessions_filename = g_string_new(basedir->str); g_string_append(omemo_ctx.sessions_filename, "sessions.txt"); @@ -207,6 +212,7 @@ omemo_on_connect(ProfAccount *account) omemo_devicelist_subscribe(); omemo_ctx.identity_keyfile = g_key_file_new(); + omemo_ctx.trust_keyfile = g_key_file_new(); omemo_ctx.sessions_keyfile = g_key_file_new(); if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { @@ -216,13 +222,19 @@ omemo_on_connect(ProfAccount *account) return; } + error = NULL; + if (g_key_file_load_from_file(omemo_ctx.trust_keyfile, omemo_ctx.trust_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { + _load_trust(); + } else if (error->code != G_FILE_ERROR_NOENT) { + log_warning("OMEMO: error loading trust from: %s, %s", omemo_ctx.sessions_filename->str, error->message); + } + error = NULL; if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { _load_sessions(); } else if (error->code != G_FILE_ERROR_NOENT) { log_warning("OMEMO: error loading sessions from: %s, %s", omemo_ctx.sessions_filename->str, error->message); } - } void @@ -243,6 +255,8 @@ omemo_on_disconnect(void) g_string_free(omemo_ctx.identity_filename, TRUE); g_key_file_free(omemo_ctx.identity_keyfile); + g_string_free(omemo_ctx.trust_filename, TRUE); + g_key_file_free(omemo_ctx.trust_keyfile); g_string_free(omemo_ctx.sessions_filename, TRUE); g_key_file_free(omemo_ctx.sessions_keyfile); } @@ -472,6 +486,22 @@ omemo_identity_keyfile_save(void) } } +GKeyFile * +omemo_trust_keyfile(void) +{ + return omemo_ctx.trust_keyfile; +} + +void +omemo_trust_keyfile_save(void) +{ + GError *error = NULL; + + if (!g_key_file_save_to_file(omemo_ctx.trust_keyfile, omemo_ctx.trust_filename->str, &error)) { + log_error("OMEMO: error saving trust to: %s, %s", omemo_ctx.trust_filename->str, error->message); + } +} + GKeyFile * omemo_sessions_keyfile(void) { @@ -1039,14 +1069,19 @@ omemo_untrust(const char *const jid, const char *const fingerprint_formatted) GHashTableIter iter; gpointer key, value; - g_hash_table_iter_init(&iter, omemo_ctx.identity_key_store.trusted); + GHashTable *trusted = g_hash_table_lookup(omemo_ctx.identity_key_store.trusted, jid); + if (!trusted) { + return; + } + + g_hash_table_iter_init(&iter, trusted); while (g_hash_table_iter_next(&iter, &key, &value)) { signal_buffer *buffer = value; unsigned char *original = signal_buffer_data(buffer); /* Skip DJB_TYPE byte */ original++; if ((signal_buffer_len(buffer) - 1) == len && memcmp(original, fingerprint, len) == 0) { - g_hash_table_remove(omemo_ctx.identity_key_store.trusted, key); + g_hash_table_remove(trusted, key); } } free(fingerprint); @@ -1214,27 +1249,43 @@ _load_identity(void) _generate_signed_pre_key(); } - /* Trusted keys */ - keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, NULL, NULL); - if (keys) { - int i; - for (i = 0; keys[i] != NULL; i++) { - char *key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_TRUST, keys[i], NULL); - size_t key_len; - unsigned char *key = g_base64_decode(key_b64, &key_len); - g_free(key_b64); - signal_buffer *buffer = signal_buffer_create(key, key_len); - g_free(key); - g_hash_table_insert(omemo_ctx.identity_key_store.trusted, strdup(keys[i]), buffer); - } - } - g_strfreev(keys); - loaded = TRUE; omemo_identity_keyfile_save(); } +static void +_load_trust(void) +{ + char **keys = NULL; + char **groups = g_key_file_get_groups(omemo_ctx.trust_keyfile, NULL); + if (groups) { + int i; + for (i = 0; groups[i] != NULL; i++) { + + keys = g_key_file_get_keys(omemo_ctx.trust_keyfile, groups[i], NULL, NULL); + int j; + for (j = 0; keys[j] != NULL; j++) { + char *key_b64 = g_key_file_get_string(omemo_ctx.trust_keyfile, groups[i], keys[j], NULL); + size_t key_len; + unsigned char *key = g_base64_decode(key_b64, &key_len); + g_free(key_b64); + signal_buffer *buffer = signal_buffer_create(key, key_len); + g_free(key); + uint32_t device_id = strtoul(keys[j], NULL, 10); + GHashTable *trusted = g_hash_table_lookup(omemo_ctx.identity_key_store.trusted, groups[i]); + if (!trusted) { + trusted = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); + g_hash_table_insert(omemo_ctx.identity_key_store.trusted, strdup(groups[i]), trusted); + } + g_hash_table_insert(trusted, GINT_TO_POINTER(device_id), buffer); + } + g_strfreev(keys); + } + g_strfreev(groups); + } +} + static void _load_sessions(void) { @@ -1264,7 +1315,7 @@ _load_sessions(void) } g_strfreev(keys); } - free(groups); + g_strfreev(groups); } } diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 72e27429..e9186dfa 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -31,6 +31,8 @@ void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); GKeyFile *omemo_identity_keyfile(void); void omemo_identity_keyfile_save(void); +GKeyFile *omemo_trust_keyfile(void); +void omemo_trust_keyfile_save(void); GKeyFile *omemo_sessions_keyfile(void); void omemo_sessions_keyfile_save(void); char *omemo_format_fingerprint(const char *const fingerprint); diff --git a/src/omemo/store.c b/src/omemo/store.c index 3520db6d..e380e12b 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -317,18 +317,24 @@ save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data) { identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; - char *node = g_strdup_printf("%s:%d", address->name, address->device_id); signal_buffer *buffer = signal_buffer_create(key_data, key_len); - g_hash_table_insert(identity_key_store->trusted, strdup(node), buffer); + + GHashTable *trusted = g_hash_table_lookup(identity_key_store->trusted, strdup(address->name)); + if (!trusted) { + trusted = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); + g_hash_table_insert(identity_key_store->trusted, strdup(address->name), trusted); + } + g_hash_table_insert(trusted, GINT_TO_POINTER(address->device_id), buffer); /* Long term storage */ char *key_b64 = g_base64_encode(key_data, key_len); - g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_TRUST, node, key_b64); + char *device_id = g_strdup_printf("%d", address->device_id); + g_key_file_set_string(omemo_trust_keyfile(), address->name, strdup(device_id), key_b64); + g_free(device_id); g_free(key_b64); - omemo_identity_keyfile_save(); - g_free(node); + omemo_trust_keyfile_save(); return SG_SUCCESS; } @@ -339,11 +345,14 @@ is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, { int ret; identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; - char *node = g_strdup_printf("%s:%d", address->name, address->device_id); + + GHashTable *trusted = g_hash_table_lookup(identity_key_store->trusted, address->name); + if (!trusted) { + return 0; + } signal_buffer *buffer = signal_buffer_create(key_data, key_len); - signal_buffer *original = g_hash_table_lookup(identity_key_store->trusted, node); - g_free(node); + signal_buffer *original = g_hash_table_lookup(trusted, GINT_TO_POINTER(address->device_id)); ret = original != NULL && signal_buffer_compare(buffer, original) == 0; diff --git a/src/omemo/store.h b/src/omemo/store.h index 598f1bfa..d4096c90 100644 --- a/src/omemo/store.h +++ b/src/omemo/store.h @@ -5,7 +5,6 @@ #define OMEMO_STORE_GROUP_IDENTITY "identity" #define OMEMO_STORE_GROUP_PREKEYS "prekeys" #define OMEMO_STORE_GROUP_SIGNED_PREKEYS "signed_prekeys" -#define OMEMO_STORE_GROUP_TRUST "trust" #define OMEMO_STORE_KEY_DEVICE_ID "device_id" #define OMEMO_STORE_KEY_REGISTRATION_ID "registration_id" #define OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC "identity_key_public" -- cgit 1.4.1-2-gfad0 From 0bc660400b9a62ae2982b1ec6c374de13ec54477 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Tue, 9 Apr 2019 08:22:35 +0320 Subject: Automatically starts OMEMO if one identity is trusted --- src/command/cmd_funcs.c | 50 ++++++++++++++++++++++++++++++++++++++ src/omemo/omemo.c | 29 +++++++++++++++++----- src/omemo/omemo.h | 1 + tests/unittests/omemo/stub_omemo.c | 6 +++++ 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index e1f62ace..b004c4e1 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -2150,17 +2150,67 @@ cmd_msg(ProfWin *window, const char *const command, gchar **args) } ui_focus_win((ProfWin*)chatwin); +#ifdef HAVE_OMEMO +#ifndef HAVE_LIBOTR + if (omemo_is_trusted_jid(barejid)) { + omemo_start_session(barejid); + chatwin->is_omemo = TRUE; + } + + if (msg) { + cl_ev_send_msg(chatwin, msg, NULL); + } + + return TRUE; +#endif +#endif + +#ifdef HAVE_OMEMO +#ifdef HAVE_LIBOTR + if (omemo_is_trusted_jid(barejid) && otr_is_secure(barejid)) { + win_println(window, THEME_DEFAULT, '!', "Chat could be either OMEMO or OTR encrypted. Use '/omemo start %s' or '/otr start %s' to start a session.", usr, usr); + return TRUE; + } else if (omemo_is_trusted_jid(barejid)) { + omemo_start_session(barejid); + chatwin->is_omemo = TRUE; + } + if (msg) { cl_ev_send_msg(chatwin, msg, NULL); } else { + if (otr_is_secure(barejid)) { + chatwin_otr_secured(chatwin, otr_is_trusted(barejid)); + } + } + + return TRUE; +#endif +#endif + +#ifndef HAVE_OMEMO #ifdef HAVE_LIBOTR + if (msg) { + cl_ev_send_msg(chatwin, msg, NULL); + } else { if (otr_is_secure(barejid)) { chatwin_otr_secured(chatwin, otr_is_trusted(barejid)); } + } + + return TRUE; #endif +#endif + +#ifndef HAVE_OMEMO +#ifndef HAVE_LIBOTR + if (msg) { + cl_ev_send_msg(chatwin, msg, NULL); } return TRUE; +#endif +#endif + } } diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 377a2637..1d641e4c 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -909,6 +909,21 @@ omemo_known_device_identities(const char *const jid) return g_hash_table_get_keys(known_identities); } +gboolean +omemo_is_trusted_jid(const char *const jid) +{ + GHashTable *trusted = g_hash_table_lookup(omemo_ctx.identity_key_store.trusted, jid); + if (!trusted) { + return FALSE; + } + + if (g_hash_table_size(trusted) > 0) { + return TRUE; + } + + return FALSE; +} + gboolean omemo_is_trusted_identity(const char *const jid, const char *const fingerprint) { @@ -1262,6 +1277,13 @@ _load_trust(void) if (groups) { int i; for (i = 0; groups[i] != NULL; i++) { + GHashTable *trusted; + + trusted = g_hash_table_lookup(omemo_ctx.identity_key_store.trusted, groups[i]); + if (!trusted) { + trusted = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); + g_hash_table_insert(omemo_ctx.identity_key_store.trusted, strdup(groups[i]), trusted); + } keys = g_key_file_get_keys(omemo_ctx.trust_keyfile, groups[i], NULL, NULL); int j; @@ -1273,11 +1295,6 @@ _load_trust(void) signal_buffer *buffer = signal_buffer_create(key, key_len); g_free(key); uint32_t device_id = strtoul(keys[j], NULL, 10); - GHashTable *trusted = g_hash_table_lookup(omemo_ctx.identity_key_store.trusted, groups[i]); - if (!trusted) { - trusted = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); - g_hash_table_insert(omemo_ctx.identity_key_store.trusted, strdup(groups[i]), trusted); - } g_hash_table_insert(trusted, GINT_TO_POINTER(device_id), buffer); } g_strfreev(keys); @@ -1299,7 +1316,7 @@ _load_sessions(void) device_store = g_hash_table_lookup(omemo_ctx.session_store, groups[i]); if (!device_store) { device_store = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); - g_hash_table_insert(omemo_ctx.session_store, groups[i], device_store); + g_hash_table_insert(omemo_ctx.session_store, strdup(groups[i]), device_store); } char **keys = g_key_file_get_keys(omemo_ctx.sessions_keyfile, groups[i], NULL, NULL); diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index e9186dfa..dcae9266 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -40,6 +40,7 @@ char *omemo_own_fingerprint(gboolean formatted); 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_jid(const char *const jid); gboolean omemo_is_trusted_identity(const char *const jid, const char *const fingerprint); char *omemo_fingerprint_autocomplete(const char *const search_str, gboolean previous); void omemo_fingerprint_autocomplete_reset(void); diff --git a/tests/unittests/omemo/stub_omemo.c b/tests/unittests/omemo/stub_omemo.c index 84398378..5b1d02df 100644 --- a/tests/unittests/omemo/stub_omemo.c +++ b/tests/unittests/omemo/stub_omemo.c @@ -21,6 +21,12 @@ omemo_format_fingerprint(const char *const fingerprint) void omemo_generate_crypto_materials(ProfAccount *account) {} +gboolean +omemo_is_trusted_jid(const char *const jid) +{ + return TRUE; +} + gboolean omemo_is_trusted_identity(const char *const jid, const char *const fingerprint) { -- cgit 1.4.1-2-gfad0 From 277e8dc901140a40416dbf50bf6862b2980b5ec6 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 10 Apr 2019 06:49:20 +0320 Subject: Add /omemo char command --- src/command/cmd_defs.c | 6 +++++- src/command/cmd_funcs.c | 19 +++++++++++++++++++ src/command/cmd_funcs.h | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index ab4112c7..2c790a73 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2341,6 +2341,7 @@ static struct cmd_t command_defs[] = { "trust", cmd_omemo_trust }, { "untrust", cmd_omemo_untrust }, { "fingerprint", cmd_omemo_fingerprint }, + { "char", cmd_omemo_char }, { "clear_device_list", cmd_omemo_clear_device_list }) CMD_NOMAINFUNC CMD_TAGS( @@ -2353,6 +2354,7 @@ static struct cmd_t command_defs[] = "/omemo trust [] ", "/omemo end", "/omemo fingerprint []", + "/omemo char ", "/omemo clear_device_list") CMD_DESC( "Omemo commands to manage keys, and perform encryption during chat sessions.") @@ -2363,12 +2365,14 @@ static struct cmd_t command_defs[] = { "log on|off", "Enable or disable plaintext logging of OMEMO encrypted messages." }, { "log redact", "Log OMEMO encrypted messages, but replace the contents with [redacted]. This is the default." }, { "fingerprint", "Show contact fingerprints." }, + { "char ", "Set the character to be displayed next to OMEMO encrypted messages." }, { "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."}) CMD_EXAMPLES( "/omemo gen", "/omemo start buddy@buddychat.org", "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", - "/omemo untrust buddy@buddychat.org c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a") + "/omemo untrust buddy@buddychat.org c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", + "/omemo char *") }, }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index b004c4e1..40952c20 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8050,6 +8050,25 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) #endif } +gboolean +cmd_omemo_char(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + } else if (strlen(args[1]) != 1) { + cons_bad_cmd_usage(command); + } else { + prefs_set_omemo_char(args[1][0]); + cons_show("OMEMO char set to %c.", args[1][0]); + } + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} + gboolean cmd_omemo_log(ProfWin *window, const char *const command, gchar **args) { diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 3410075d..249b50fe 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -215,6 +215,7 @@ gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args) gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_char(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_log(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args); -- cgit 1.4.1-2-gfad0 From 381d85bccaaffea1b20f17e9441a350baf9a7136 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 10 Apr 2019 07:41:09 +0320 Subject: Fix build on travis --- src/xmpp/xmpp.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 54a56f02..d5330599 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -35,6 +35,8 @@ #ifndef XMPP_XMPP_H #define XMPP_XMPP_H +#include + #include "config.h" #ifdef HAVE_LIBMESODE -- cgit 1.4.1-2-gfad0 From f9b2fdc7dba001cbff53a24b5e470f5db1e66121 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 10 Apr 2019 07:42:45 +0320 Subject: OMEMO should be written uppercase --- configure.ac | 10 +++++----- src/command/cmd_defs.c | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index ea1e9f14..06daa8d9 100644 --- a/configure.ac +++ b/configure.ac @@ -61,7 +61,7 @@ AC_ARG_ENABLE([otr], AC_ARG_ENABLE([pgp], [AS_HELP_STRING([--enable-pgp], [enable pgp])]) AC_ARG_ENABLE([omemo], - [AS_HELP_STRING([--enable-omemo], [enable omemo encryption])]) + [AS_HELP_STRING([--enable-omemo], [enable OMEMO encryption])]) AC_ARG_WITH([xscreensaver], [AS_HELP_STRING([--with-xscreensaver], [use libXScrnSaver to determine idle time])]) AC_ARG_WITH([themes], @@ -277,15 +277,15 @@ if test "x$enable_omemo" != xno; then AC_DEFINE([HAVE_LIBSIGNAL_LT_2_3_2], [1], [Have libsignal-protocol-c < 2.3.2])], [AM_CONDITIONAL([BUILD_OMEMO], [false]) AS_IF([test "x$enable_omemo" = xyes], - [AC_MSG_ERROR([libsignal-protocol-c is required for omemo support])], - [AC_MSG_NOTICE([libsignal-protocol-c not found, omemo support not enabled])])])]) + [AC_MSG_ERROR([libsignal-protocol-c is required for OMEMO support])], + [AC_MSG_NOTICE([libsignal-protocol-c not found, OMEMO support not enabled])])])]) AC_CHECK_LIB([gcrypt], [gcry_check_version], [LIBS="-lgcrypt $LIBS"], [AM_CONDITIONAL([BUILD_OMEMO], [false]) AS_IF([test "x$enable_omemo" = xyes], - [AC_MSG_ERROR([gcrypt is required for omemo support])], - [AC_MSG_NOTICE([gcrypt not found, omemo support not enabled])])]) + [AC_MSG_ERROR([gcrypt is required for OMEMO support])], + [AC_MSG_NOTICE([gcrypt not found, OMEMO support not enabled])])]) AM_COND_IF([BUILD_OMEMO], [AC_DEFINE([HAVE_OMEMO], [1], [Have OMEMO])]) fi diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 2c790a73..ee86aaba 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2357,16 +2357,16 @@ static struct cmd_t command_defs[] = "/omemo char ", "/omemo clear_device_list") CMD_DESC( - "Omemo commands to manage keys, and perform encryption during chat sessions.") - CMD_ARGS( - { "gen", "Generate OMEMO crytographic materials for current account." }, - { "start []", "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]. This is the default." }, - { "fingerprint", "Show contact fingerprints." }, - { "char ", "Set the character to be displayed next to OMEMO encrypted messages." }, - { "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."}) + "OMEMO commands to manage keys, and perform encryption during chat sessions.") + CMD_ARGS( + { "gen", "Generate OMEMO crytographic materials for current account." }, + { "start []", "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]. This is the default." }, + { "fingerprint []", "Show contact fingerprints, or current recipient if omitted." }, + { "char ", "Set the character to be displayed next to OMEMO encrypted messages." }, + { "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."}) CMD_EXAMPLES( "/omemo gen", "/omemo start buddy@buddychat.org", -- cgit 1.4.1-2-gfad0 From df648ba959621905769674e861d44d527e09e249 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 10 Apr 2019 18:31:15 +0320 Subject: Try to start sessions with every jid in roster --- src/event/server_events.c | 4 ++++ src/omemo/omemo.c | 17 +++++++++++++++++ src/omemo/omemo.h | 1 + 3 files changed, 22 insertions(+) diff --git a/src/event/server_events.c b/src/event/server_events.c index 7c1cfd78..36db8ebe 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -174,6 +174,10 @@ sv_ev_roster_received(void) const char *fulljid = connection_get_fulljid(); plugins_on_connect(account_name, fulljid); + +#ifdef HAVE_OMEMO + omemo_start_sessions(); +#endif } void diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 1d641e4c..7b3855dd 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -23,6 +23,7 @@ #include "xmpp/connection.h" #include "xmpp/muc.h" #include "xmpp/omemo.h" +#include "xmpp/roster_list.h" #include "xmpp/xmpp.h" static gboolean loaded; @@ -304,6 +305,7 @@ omemo_generate_crypto_materials(ProfAccount *account) loaded = TRUE; omemo_publish_crypto_materials(); + omemo_start_sessions(); } void @@ -326,6 +328,20 @@ omemo_publish_crypto_materials(void) jid_destroy(jid); } +void +omemo_start_sessions(void) +{ + GSList *contacts = roster_get_contacts(ROSTER_ORD_NAME); + if (contacts) { + GSList *curr = contacts; + for (curr = contacts; curr != NULL; curr = g_slist_next(curr)){ + PContact contact = curr->data; + const char *jid = p_contact_barejid(contact); + omemo_start_session(jid); + } + } +} + void omemo_start_session(const char *const barejid) { @@ -1267,6 +1283,7 @@ _load_identity(void) loaded = TRUE; omemo_identity_keyfile_save(); + omemo_start_sessions(); } static void diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index dcae9266..166a5292 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -45,6 +45,7 @@ gboolean omemo_is_trusted_identity(const char *const jid, const char *const fing char *omemo_fingerprint_autocomplete(const char *const search_str, gboolean previous); void omemo_fingerprint_autocomplete_reset(void); +void omemo_start_sessions(void); void omemo_start_session(const char *const barejid); 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); -- cgit 1.4.1-2-gfad0 From ad21021ce7905b500d6302964aecb3911c8e2157 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 10 Apr 2019 18:58:06 +0320 Subject: Fix issue with first encrypted message When decrypting first message with prekey, libsignal wants to remove used prekey from storage. Return value on success should be 0. We used to return number of deleted keys. Thus libsignal was considering we failed to remove the key and we were ignoring plaintext. --- src/omemo/store.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/omemo/store.c b/src/omemo/store.c index e380e12b..76b7449c 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -225,7 +225,11 @@ remove_pre_key(uint32_t pre_key_id, void *user_data) omemo_identity_keyfile_save(); - return ret; + if (ret > 0) { + return SG_SUCCESS; + } else { + return SG_ERR_INVALID_KEY_ID; + } } int -- cgit 1.4.1-2-gfad0 From f75e1d7a7b05c68f03b6b13163ac9f2b8824e7df Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Wed, 10 Apr 2019 19:03:12 +0320 Subject: Support contact name in /omemo fingerprint command --- src/command/cmd_funcs.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 40952c20..fe289f0b 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -8169,10 +8169,15 @@ cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args) return TRUE; } } else { - jid = jid_create(args[1]); - if (!jid) { - cons_show("%s is not a valid jid", args[1]); - return TRUE; + char *barejid = roster_barejid_from_name(args[1]); + if (barejid) { + jid = jid_create(barejid); + } else { + jid = jid_create(args[1]); + if (!jid) { + cons_show("%s is not a valid jid", args[1]); + return TRUE; + } } } -- cgit 1.4.1-2-gfad0