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 --- src/command/cmd_defs.c | 20 +++++++++++++++++++- src/command/cmd_funcs.c | 23 +++++++++++++++++++++++ src/command/cmd_funcs.h | 2 ++ 3 files changed, 44 insertions(+), 1 deletion(-) (limited to 'src/command') 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 -- 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(-) (limited to 'src/command') 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 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(+) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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 (limited to 'src/command') 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 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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 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(+) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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(+) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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(+) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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(+) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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 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(+) (limited to 'src/command') 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 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(+) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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(+) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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 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(-) (limited to 'src/command') 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