From 8759d9659d99620cf00ab64baf5dcf948bdae402 Mon Sep 17 00:00:00 2001 From: Tony Olagbaiye Date: Mon, 10 Jan 2022 20:40:05 +0000 Subject: [PATCH] omemo progress --- .envrc | 9 +- account.c | 5 +- channel.c | 174 +++++--- channel.h | 21 +- command.c | 103 ++++- connection.c | 555 ++++++++++++++---------- input.c | 10 +- libomemo.scm | 2 +- omemo.c | 1131 ++++++++++++++++++++++++++++++++++++++----------- omemo.h | 6 +- xmpp/iq.c | 55 +++ xmpp/stanza.h | 6 + 12 files changed, 1508 insertions(+), 569 deletions(-) diff --git a/.envrc b/.envrc index 7d42a8c..fc35e69 100644 --- a/.envrc +++ b/.envrc @@ -30,10 +30,12 @@ use_guix() # Miscellaneous packages. ENVIRONMENTS=( weechat # Debug runs + profanity # Test ) # Environment packages. PACKAGES=( + profanity # Test autoconf # Deps with autoreconf autoconf-archive # Deps with m4 tooling automake # Deps with automake @@ -49,10 +51,9 @@ use_guix() libxml2 # Dep (libxml2) libstrophe # Dep (strophe) libgcrypt # Dep (gcrypt) - -l libomemo.scm #libsignal-protocol-c # Dep (libsignal) lmdb # Dep (lmdb) rnp # Dep (rnpgp) - glib sqlite minixml + libsignal-protocol-c -l libomemo.scm # Dep (libsignal) ) # Thanks @@ -62,9 +63,11 @@ use_guix() } use guix \ + --with-debug-info=profanity\ --with-debug-info=weechat\ --with-debug-info=libstrophe\ --with-debug-info=libsignal-protocol-c\ + --with-debug-info=libomemo-c\ --with-debug-info=lmdb\ --with-debug-info=rnp\ - clang:extra gdb + clang:extra gdb lmdb diff --git a/account.c b/account.c index 883b553..4c10c27 100644 --- a/account.c +++ b/account.c @@ -186,8 +186,7 @@ xmpp_stanza_t *account__get_devicelist(struct t_account *account) children = malloc(sizeof(xmpp_stanza_t *) * 128); children[i++] = stanza__iq_pubsub_publish_item_list_device( - account->context, NULL, with_noop(device->name), - with_noop("weechat")); + account->context, NULL, with_noop(device->name), NULL); free(device->label); free(device->name); @@ -207,7 +206,7 @@ xmpp_stanza_t *account__get_devicelist(struct t_account *account) account->context, NULL, children, with_noop(node)); children[1] = NULL; children[0] = stanza__iq_pubsub_publish_item( - account->context, NULL, children, NULL); + account->context, NULL, children, with_noop("current")); node = "eu.siacs.conversations.axolotl.devicelist"; children[0] = stanza__iq_pubsub_publish(account->context, NULL, children, diff --git a/channel.c b/channel.c index a3e289b..4b81b97 100644 --- a/channel.c +++ b/channel.c @@ -11,8 +11,8 @@ #include #include "plugin.h" -#include "omemo.h" #include "account.h" +#include "omemo.h" #include "user.h" #include "channel.h" #include "input.h" @@ -24,17 +24,48 @@ const char *channel__transport_name(enum t_channel_transport transport) { switch (transport) { - case CHANNEL_TRANSPORT_PLAINTEXT: + case CHANNEL_TRANSPORT_PLAIN: return "PLAINTEXT"; case CHANNEL_TRANSPORT_OMEMO: return "OMEMO"; case CHANNEL_TRANSPORT_PGP: return "PGP"; + case CHANNEL_TRANSPORT_OTR: + return "OTR"; default: return NULL; } } +void channel__set_transport(struct t_channel *channel, enum t_channel_transport transport, int force) +{ + if (force) + switch (transport) + { + case CHANNEL_TRANSPORT_PLAIN: + channel->omemo.enabled = 0; + channel->pgp.enabled = 0; + break; + case CHANNEL_TRANSPORT_OMEMO: + channel->omemo.enabled = 1; + channel->pgp.enabled = 0; + break; + case CHANNEL_TRANSPORT_PGP: + channel->omemo.enabled = 0; + channel->pgp.enabled = 1; + break; + default: + break; + } + if (channel->transport != transport) + { + channel->transport = transport; + weechat_printf_date_tags(channel->buffer, 0, NULL, "%s%sTransport: %s", + weechat_prefix("network"), weechat_color("gray"), + channel__transport_name(channel->transport)); + } +} + struct t_account *channel__account(struct t_channel *channel) { struct t_account *ptr_account; @@ -82,7 +113,7 @@ struct t_gui_buffer *channel__search_buffer(struct t_account *account, { struct t_hdata *hdata_buffer; struct t_gui_buffer *ptr_buffer; - const char *ptr_type, *ptr_account_name, *ptr_channel_name; + const char *ptr_type, *ptr_account_name, *ptr_remote_jid; hdata_buffer = weechat_hdata_get("buffer"); ptr_buffer = weechat_hdata_get_list(hdata_buffer, "gui_buffers"); @@ -93,18 +124,18 @@ struct t_gui_buffer *channel__search_buffer(struct t_account *account, { ptr_type = weechat_buffer_get_string(ptr_buffer, "localvar_type"); ptr_account_name = weechat_buffer_get_string(ptr_buffer, - "localvar_server"); - ptr_channel_name = weechat_buffer_get_string(ptr_buffer, - "localvar_channel"); + "localvar_account"); + ptr_remote_jid = weechat_buffer_get_string(ptr_buffer, + "localvar_remote_jid"); if (ptr_type && ptr_type[0] && ptr_account_name && ptr_account_name[0] - && ptr_channel_name && ptr_channel_name[0] + && ptr_remote_jid && ptr_remote_jid[0] && ( (( (type == CHANNEL_TYPE_MUC)) - && (strcmp(ptr_type, "channel") == 0)) + && (strcmp(ptr_type, "room") == 0)) || (( (type == CHANNEL_TYPE_PM)) && (strcmp(ptr_type, "private") == 0))) && (strcmp(ptr_account_name, account->name) == 0) - && (weechat_strcasecmp(ptr_channel_name, name) == 0)) + && (weechat_strcasecmp(ptr_remote_jid, name) == 0)) { return ptr_buffer; } @@ -121,13 +152,13 @@ struct t_gui_buffer *channel__create_buffer(struct t_account *account, { struct t_gui_buffer *ptr_buffer; int buffer_created; - const char *short_name, *localvar_channel; - char buffer_name[256]; + const char *short_name, *localvar_remote_jid; + char buffer_name[1024]; buffer_created = 0; snprintf(buffer_name, sizeof(buffer_name), - "xmpp.%s.%s", account->name, name); + "%s.%s", account->name, name); ptr_buffer = channel__search_buffer(account, type, name); if (ptr_buffer) @@ -152,12 +183,12 @@ struct t_gui_buffer *channel__create_buffer(struct t_account *account, } else { - short_name = weechat_buffer_get_string (ptr_buffer, "short_name"); - localvar_channel = weechat_buffer_get_string(ptr_buffer, - "localvar_channel"); + short_name = weechat_buffer_get_string(ptr_buffer, "short_name"); + localvar_remote_jid = weechat_buffer_get_string(ptr_buffer, + "localvar_remote_jid"); if (!short_name || - (localvar_channel && (strcmp(localvar_channel, short_name) == 0))) + (localvar_remote_jid && (strcmp(localvar_remote_jid, short_name) == 0))) { weechat_buffer_set(ptr_buffer, "short_name", xmpp_jid_node(account->context, name)); @@ -167,22 +198,21 @@ struct t_gui_buffer *channel__create_buffer(struct t_account *account, account_option_set(account, ACCOUNT_OPTION_NICKNAME, xmpp_jid_node(account->context, account_jid(account))); - weechat_buffer_set(ptr_buffer, "name", name); weechat_buffer_set(ptr_buffer, "notify", (type == CHANNEL_TYPE_PM) ? "3" : "1"); weechat_buffer_set(ptr_buffer, "localvar_set_type", (type == CHANNEL_TYPE_PM) ? "private" : "channel"); weechat_buffer_set(ptr_buffer, "localvar_set_nick", account_nickname(account)); - weechat_buffer_set(ptr_buffer, "localvar_set_server", account->name); - weechat_buffer_set(ptr_buffer, "localvar_set_channel", name); + weechat_buffer_set(ptr_buffer, "localvar_set_account", account->name); + weechat_buffer_set(ptr_buffer, "localvar_set_remote_jid", name); weechat_buffer_set(ptr_buffer, "input_multiline", "1"); if (buffer_created) { - (void) weechat_hook_signal_send ("logger_backlog", - WEECHAT_HOOK_SIGNAL_POINTER, - ptr_buffer); + (void) weechat_hook_signal_send("logger_backlog", + WEECHAT_HOOK_SIGNAL_POINTER, + ptr_buffer); weechat_buffer_set(ptr_buffer, "input_get_unknown_commands", "1"); if (type != CHANNEL_TYPE_PM) { @@ -275,13 +305,15 @@ struct t_channel *channel__new(struct t_account *account, new_channel->type = type; new_channel->id = strdup(id); new_channel->name = strdup(name); - new_channel->transport = CHANNEL_TRANSPORT_PLAINTEXT; - new_channel->pgp_id = NULL; - new_channel->omemo.session = 0; + new_channel->transport = CHANNEL_TRANSPORT_PLAIN; + new_channel->omemo.enabled = type == CHANNEL_TYPE_PM ? 1 : 0; new_channel->omemo.devicelist_requests = weechat_hashtable_new(64, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_POINTER, NULL, NULL); new_channel->omemo.bundle_requests = weechat_hashtable_new(64, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_POINTER, NULL, NULL); + new_channel->pgp.enabled = 1; + new_channel->pgp.id = NULL; + new_channel->otr.enabled = 0; new_channel->topic.value = NULL; new_channel->topic.creator = NULL; @@ -797,8 +829,8 @@ void channel__free(struct t_account *account, free(channel->id); if (channel->name) free(channel->name); - if (channel->pgp_id) - free(channel->pgp_id); + if (channel->pgp.id) + free(channel->pgp.id); if (channel->topic.value) free(channel->topic.value); if (channel->topic.creator) @@ -994,8 +1026,8 @@ struct t_channel_member *channel__remove_member(struct t_account *account, return member; } -void channel__send_message(struct t_account *account, struct t_channel *channel, - const char *to, const char *body) +int channel__send_message(struct t_account *account, struct t_channel *channel, + const char *to, const char *body) { channel__send_reads(account, channel); @@ -1008,30 +1040,51 @@ void channel__send_message(struct t_account *account, struct t_channel *channel, xmpp_stanza_set_id(message, id); xmpp_free(account->context, id); - if (account->omemo && channel->omemo.session >= 0) + if (account->omemo && channel->omemo.enabled) { - xmpp_message_set_body(message, body); - - if (channel->transport != CHANNEL_TRANSPORT_OMEMO) + xmpp_stanza_t *encrypted = omemo__encode(account, to, body); + if (!encrypted) { - channel->transport = CHANNEL_TRANSPORT_OMEMO; - weechat_printf_date_tags(channel->buffer, 0, NULL, "%s%sTransport: %s", - weechat_prefix("network"), weechat_color("gray"), - channel__transport_name(channel->transport)); + weechat_printf_date_tags(channel->buffer, 0, "notify_none", "%s%s", + weechat_prefix("error"), "OMEMO Error"); + channel__set_transport(channel, CHANNEL_TRANSPORT_PLAIN, 1); + xmpp_stanza_release(message); + return WEECHAT_RC_ERROR; } + xmpp_stanza_add_child(message, encrypted); + xmpp_stanza_release(encrypted); - omemo__encode(account->omemo, to, 0, body); + xmpp_stanza_t *message__encryption = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__encryption, "encryption"); + xmpp_stanza_set_ns(message__encryption, "urn:xmpp:eme:0"); + xmpp_stanza_set_attribute(message__encryption, "namespace", + "eu.siacs.conversations.axolotl"); + xmpp_stanza_set_attribute(message__encryption, "name", "OMEMO"); + xmpp_stanza_add_child(message, message__encryption); + xmpp_stanza_release(message__encryption); + + xmpp_message_set_body(message, OMEMO_ADVICE); + + channel__set_transport(channel, CHANNEL_TRANSPORT_OMEMO, 0); } - else if (channel->pgp_id) + else if (channel->pgp.enabled && channel->pgp.id) { xmpp_stanza_t *message__x = xmpp_stanza_new(account->context); xmpp_stanza_set_name(message__x, "x"); xmpp_stanza_set_ns(message__x, "jabber:x:encrypted"); xmpp_stanza_t *message__x__text = xmpp_stanza_new(account->context); - char *ciphertext = pgp__encrypt(channel->buffer, account->pgp, account_pgp_keyid(account), channel->pgp_id, body); + char *ciphertext = pgp__encrypt(channel->buffer, account->pgp, account_pgp_keyid(account), channel->pgp.id, body); if (ciphertext) xmpp_stanza_set_text(message__x__text, ciphertext); + else + { + weechat_printf_date_tags(channel->buffer, 0, "notify_none", "%s%s", + weechat_prefix("error"), "PGP Error"); + channel__set_transport(channel, CHANNEL_TRANSPORT_PLAIN, 1); + xmpp_stanza_release(message); + return WEECHAT_RC_ERROR; + } free(ciphertext); xmpp_stanza_add_child(message__x, message__x__text); @@ -1050,29 +1103,17 @@ void channel__send_message(struct t_account *account, struct t_channel *channel, xmpp_message_set_body(message, PGP_ADVICE); - if (ciphertext && channel->transport != CHANNEL_TRANSPORT_PGP) - { - channel->transport = CHANNEL_TRANSPORT_PGP; - weechat_printf_date_tags(channel->buffer, 0, NULL, "%s%sTransport: %s", - weechat_prefix("network"), weechat_color("gray"), - channel__transport_name(channel->transport)); - } + channel__set_transport(channel, CHANNEL_TRANSPORT_PGP, 0); } else { xmpp_message_set_body(message, body); - if (channel->transport != CHANNEL_TRANSPORT_PLAINTEXT) - { - channel->transport = CHANNEL_TRANSPORT_PLAINTEXT; - weechat_printf_date_tags(channel->buffer, 0, NULL, "%s%sTransport: %s", - weechat_prefix("network"), weechat_color("gray"), - channel__transport_name(channel->transport)); - } + channel__set_transport(channel, CHANNEL_TRANSPORT_PLAIN, 0); } char *url = strstr(body, "http"); - if (url) + if (url && channel->transport == CHANNEL_TRANSPORT_PLAIN) { xmpp_stanza_t *message__x = xmpp_stanza_new(account->context); xmpp_stanza_set_name(message__x, "x"); @@ -1095,11 +1136,28 @@ void channel__send_message(struct t_account *account, struct t_channel *channel, xmpp_stanza_t *message__active = xmpp_stanza_new(account->context); xmpp_stanza_set_name(message__active, "active"); - xmpp_stanza_set_ns(message__active, "http://jabber./org/protocol/chatstates"); - + xmpp_stanza_set_ns(message__active, "http://jabber.org/protocol/chatstates"); xmpp_stanza_add_child(message, message__active); xmpp_stanza_release(message__active); + xmpp_stanza_t *message__request = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__request, "request"); + xmpp_stanza_set_ns(message__request, "urn:xmpp:receipts"); + xmpp_stanza_add_child(message, message__request); + xmpp_stanza_release(message__request); + + xmpp_stanza_t *message__markable = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__markable, "markable"); + xmpp_stanza_set_ns(message__markable, "urn:xmpp:chat-markers:0"); + xmpp_stanza_add_child(message, message__markable); + xmpp_stanza_release(message__markable); + + xmpp_stanza_t *message__store = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__store, "store"); + xmpp_stanza_set_ns(message__store, "urn:xmpp:hints"); + xmpp_stanza_add_child(message, message__store); + xmpp_stanza_release(message__store); + xmpp_send(account->connection, message); xmpp_stanza_release(message); if (channel->type != CHANNEL_TYPE_MUC) @@ -1108,6 +1166,8 @@ void channel__send_message(struct t_account *account, struct t_channel *channel, "%s\t%s", user__as_prefix_raw(account, account_jid(account)), body); + + return WEECHAT_RC_OK; } void channel__send_reads(struct t_account *account, struct t_channel *channel) diff --git a/channel.h b/channel.h index 9d45019..5643679 100644 --- a/channel.h +++ b/channel.h @@ -15,9 +15,11 @@ enum t_channel_type enum t_channel_transport { - CHANNEL_TRANSPORT_PLAINTEXT, + CHANNEL_TRANSPORT_PLAIN, CHANNEL_TRANSPORT_OMEMO, CHANNEL_TRANSPORT_PGP, + CHANNEL_TRANSPORT_OTR, + CHANNEL_TRANSPORT_OX, }; struct t_channel_typing @@ -63,12 +65,18 @@ struct t_channel char *id; char *name; enum t_channel_transport transport; - char *pgp_id; struct { - int session; + int enabled; struct t_hashtable *devicelist_requests; struct t_hashtable *bundle_requests; } omemo; + struct { + int enabled; + char *id; + } pgp; + struct { + int enabled; + } otr; struct t_channel_topic topic; @@ -97,6 +105,9 @@ struct t_channel const char *channel__transport_name(enum t_channel_transport transport); +void channel__set_transport(struct t_channel *channel, + enum t_channel_transport transport, int force); + struct t_account *channel__account(struct t_channel *channel); struct t_channel *channel__search(struct t_account *account, @@ -184,8 +195,8 @@ struct t_channel_member *channel__remove_member(struct t_account *account, struct t_channel *channel, const char *id, const char *reason); -void channel__send_message(struct t_account *account, struct t_channel *channel, - const char *to, const char *body); +int channel__send_message(struct t_account *account, struct t_channel *channel, + const char *to, const char *body); void channel__send_reads(struct t_account *account, struct t_channel *channel); diff --git a/command.c b/command.c index 165414c..2073a83 100644 --- a/command.c +++ b/command.c @@ -502,7 +502,7 @@ int command__enter(const void *pointer, void *data, } else { - const char *buffer_jid = weechat_buffer_get_string(buffer, "localvar_channel"); + const char *buffer_jid = weechat_buffer_get_string(buffer, "localvar_remote_jid"); pres_jid = xmpp_jid_new( ptr_account->context, @@ -757,6 +757,41 @@ int command__mam(const void *pointer, void *data, return WEECHAT_RC_OK; } +int command__omemo(const void *pointer, void *data, + struct t_gui_buffer *buffer, int argc, + char **argv, char **argv_eol) +{ + struct t_account *ptr_account = NULL; + struct t_channel *ptr_channel = NULL; + + (void) pointer; + (void) data; + (void) argc; + (void) argv; + (void) argv_eol; + + buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel); + + if (!ptr_account) + return WEECHAT_RC_ERROR; + + if (!ptr_channel) + { + weechat_printf( + ptr_account->buffer, + _("%s%s: \"%s\" command can not be executed on a account buffer"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "omemo"); + return WEECHAT_RC_OK; + } + + ptr_channel->omemo.enabled = 1; + ptr_channel->pgp.enabled = 0; + + channel__set_transport(ptr_channel, CHANNEL_TRANSPORT_OMEMO, 0); + + return WEECHAT_RC_OK; +} + int command__pgp(const void *pointer, void *data, struct t_gui_buffer *buffer, int argc, char **argv, char **argv_eol) @@ -787,13 +822,48 @@ int command__pgp(const void *pointer, void *data, { keyid = argv_eol[1]; - ptr_channel->pgp_id = strdup(keyid); + ptr_channel->pgp.id = strdup(keyid); } - else + ptr_channel->omemo.enabled = 0; + ptr_channel->pgp.enabled = 1; + + channel__set_transport(ptr_channel, CHANNEL_TRANSPORT_PGP, 0); + + return WEECHAT_RC_OK; +} + +int command__plain(const void *pointer, void *data, + struct t_gui_buffer *buffer, int argc, + char **argv, char **argv_eol) +{ + struct t_account *ptr_account = NULL; + struct t_channel *ptr_channel = NULL; + + (void) pointer; + (void) data; + (void) argc; + (void) argv; + (void) argv_eol; + + buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel); + + if (!ptr_account) + return WEECHAT_RC_ERROR; + + if (!ptr_channel) { - ptr_channel->pgp_id = NULL; + weechat_printf( + ptr_account->buffer, + _("%s%s: \"%s\" command can not be executed on a account buffer"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "plain"); + return WEECHAT_RC_OK; } + ptr_channel->omemo.enabled = 0; + ptr_channel->pgp.enabled = 0; + + channel__set_transport(ptr_channel, CHANNEL_TRANSPORT_PLAIN, 0); + return WEECHAT_RC_OK; } @@ -811,6 +881,9 @@ int command__xml(const void *pointer, void *data, buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel); + if (!ptr_account) + return WEECHAT_RC_ERROR; + if (!ptr_account->is_connected) { weechat_printf(buffer, @@ -822,7 +895,7 @@ int command__xml(const void *pointer, void *data, if (argc > 1) { stanza = xmpp_stanza_new_from_string(ptr_account->context, - argv_eol[0]); + argv_eol[1]); if (!stanza) return WEECHAT_RC_ERROR; @@ -907,15 +980,33 @@ void command__init() if (!hook) weechat_printf(NULL, "Failed to setup command /mam"); + hook = weechat_hook_command( + "omemo", + N_("set the current buffer to use omemo encryption"), + N_(""), + N_(""), + NULL, &command__omemo, NULL, NULL); + if (!hook) + weechat_printf(NULL, "Failed to setup command /omemo"); + hook = weechat_hook_command( "pgp", - N_("set the target pgp key for the current channel"), + N_("set the current buffer to use pgp encryption (with a given target pgp key)"), N_(""), N_("keyid: recipient keyid"), NULL, &command__pgp, NULL, NULL); if (!hook) weechat_printf(NULL, "Failed to setup command /pgp"); + hook = weechat_hook_command( + "plain", + N_("set the current buffer to use no encryption"), + N_(""), + N_(""), + NULL, &command__plain, NULL, NULL); + if (!hook) + weechat_printf(NULL, "Failed to setup command /plain"); + hook = weechat_hook_command( "xml", N_("send a raw xml stanza"), diff --git a/connection.c b/connection.c index 34ea101..be436c1 100644 --- a/connection.c +++ b/connection.c @@ -25,6 +25,7 @@ void connection__init() { + srand(time(NULL)); xmpp_initialize(); } @@ -87,10 +88,11 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void struct t_user *user; struct t_channel *channel; xmpp_stanza_t *pres__x_signed, *pres__x_muc_user, *show, *idle, *pres__x__item, *pres__c, *pres__status; - const char *from, *from_bare, *from_res, *type, *role = NULL, *affiliation = NULL, *jid = NULL; + const char *to, *from, *from_bare, *from_res, *type, *role = NULL, *affiliation = NULL, *jid = NULL; const char *show__text = NULL, *idle__since = NULL, *certificate = NULL, *node = NULL, *ver = NULL; char *clientid = NULL, *status; + to = xmpp_stanza_get_to(stanza); from = xmpp_stanza_get_from(stanza); if (from == NULL) return 1; @@ -115,9 +117,20 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void ver = xmpp_stanza_get_attribute(pres__c, "ver"); if (node && ver) { - int len = strlen(node)+1+strlen(ver); + int len = strlen(node)+1+strlen(ver) + 1; clientid = malloc(sizeof(char)*len); snprintf(clientid, len, "%s#%s", node, ver); + + xmpp_stanza_t *children[2] = {NULL}; + children[0] = stanza__iq_pubsub_items(account->context, NULL, + "eu.siacs.conversations.axolotl.devicelist"); + children[0] = stanza__iq_pubsub(account->context, NULL, + children, with_noop("http://jabber.org/protocol/pubsub")); + children[0] = stanza__iq(account->context, NULL, children, NULL, + strdup("fetch2"), to ? strdup(to) : NULL, strdup(from_bare), + strdup("get")); + xmpp_send(conn, children[0]); + xmpp_stanza_release(children[0]); } } pres__status = xmpp_stanza_get_child_by_name(stanza, "status"); @@ -133,8 +146,60 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void for (pres__x__item = xmpp_stanza_get_children(pres__x_muc_user); pres__x__item; pres__x__item = xmpp_stanza_get_next(pres__x__item)) { - if (weechat_strcasecmp(xmpp_stanza_get_name(pres__x__item), "item") != 0) + const char *pres__x__item__name = xmpp_stanza_get_name(pres__x__item); + if (weechat_strcasecmp(pres__x__item__name, "item") != 0) + { + if (weechat_strcasecmp(pres__x__item__name, "status") == 0) + { + const char *pres__x__status__code = xmpp_stanza_get_attribute( + pres__x__item, "code"); + switch (strtol(pres__x__status__code, NULL, 10)) + { + case 100: // Non-Anonymous: [message | Entering a room]: Inform user that any occupant is allowed to see the user's full JID + weechat_buffer_set(channel->buffer, "notify", "2"); + break; + case 101: // : [message (out of band) | Affiliation change]: Inform user that his or her affiliation changed while not in the room + break; + case 102: // : [message | Configuration change]: Inform occupants that room now shows unavailable members + break; + case 103: // : [message | Configuration change]: Inform occupants that room now does not show unavailable members + break; + case 104: // : [message | Configuration change]: Inform occupants that a non-privacy-related room configuration change has occurred + break; + case 110: // Self-Presence: [presence | Any room presence]: Inform user that presence refers to one of its own room occupants + break; + case 170: // Logging Active: [message or initial presence | Configuration change]: Inform occupants that room logging is now enabled + break; + case 171: // : [message | Configuration change]: Inform occupants that room logging is now disabled + break; + case 172: // : [message | Configuration change]: Inform occupants that the room is now non-anonymous + break; + case 173: // : [message | Configuration change]: Inform occupants that the room is now semi-anonymous + break; + case 174: // : [message | Configuration change]: Inform occupants that the room is now fully-anonymous + break; + case 201: // : [presence | Entering a room]: Inform user that a new room has been created + break; + case 210: // Nick Modified: [presence | Entering a room]: Inform user that the service has assigned or modified the occupant's roomnick + break; + case 301: // : [presence | Removal from room]: Inform user that he or she has been banned from the room + break; + case 303: // : [presence | Exiting a room]: Inform all occupants of new room nickname + break; + case 307: // : [presence | Removal from room]: Inform user that he or she has been kicked from the room + break; + case 321: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of an affiliation change + break; + case 322: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because the room has been changed to members-only and the user is not a member + break; + case 332: // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of a system shutdown + break; + default: + break; + } + } continue; + } role = xmpp_stanza_get_attribute(pres__x__item, "role"); affiliation = xmpp_stanza_get_attribute(pres__x__item, "affiliation"); @@ -156,7 +221,7 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void { user->profile.pgp_id = pgp__verify(channel->buffer, account->pgp, certificate); if (channel->type != CHANNEL_TYPE_MUC) - channel->pgp_id = user->profile.pgp_id; + channel->pgp.id = user->profile.pgp_id; } if (channel) @@ -185,7 +250,7 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void { user->profile.pgp_id = pgp__verify(channel->buffer, account->pgp, certificate); if (channel->type != CHANNEL_TYPE_MUC) - channel->pgp_id = user->profile.pgp_id; + channel->pgp.id = user->profile.pgp_id; } if (channel) @@ -328,8 +393,8 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * { if (account->omemo) { - omemo__handle_devicelist(account->omemo, from, - items); + omemo__handle_devicelist(account->omemo, + from ? from : account_jid(account), items); } children = malloc(sizeof(*children) * (3 + 1)); @@ -355,9 +420,11 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * children[0] = stanza__iq_pubsub(account->context, NULL, children, with_noop("http://jabber.org/protocol/pubsub")); + char *uuid = xmpp_uuid_gen(account->context); children[0] = - stanza__iq(account->context, NULL, children, NULL, strdup("fetch1"), - strdup(to), strdup(from), strdup("get")); + stanza__iq(account->context, NULL, children, NULL, uuid, + strdup(to), strdup(from), strdup("get")); + xmpp_free(account->context, uuid); xmpp_send(conn, children[0]); xmpp_stanza_release(children[0]); @@ -457,7 +524,7 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * "eu.siacs.conversations.axolotl"); if (encrypted && account->omemo) { - omemo__decode(account->omemo, from_bare, encrypted); + cleartext = omemo__decode(account, from_bare, encrypted); } x = xmpp_stanza_get_child_by_name_and_ns(stanza, "x", "jabber:x:encrypted"); intext = xmpp_stanza_get_text(body); @@ -650,9 +717,9 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * weechat_prefix("network"), weechat_color("gray"), channel__transport_name(channel->transport)); } - else if (!x && text == intext && channel->transport != CHANNEL_TRANSPORT_PLAINTEXT) + else if (!x && text == intext && channel->transport != CHANNEL_TRANSPORT_PLAIN) { - channel->transport = CHANNEL_TRANSPORT_PLAINTEXT; + channel->transport = CHANNEL_TRANSPORT_PLAIN; weechat_printf_date_tags(channel->buffer, date, NULL, "%s%sTransport: %s", weechat_prefix("network"), weechat_color("gray"), channel__transport_name(channel->transport)); @@ -682,232 +749,199 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * return 1; } +xmpp_stanza_t *connection__get_caps(xmpp_stanza_t *reply, struct t_account *account, char **hash) +{ + xmpp_stanza_t *query = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(query, "query"); + xmpp_stanza_set_ns(query, "http://jabber.org/protocol/disco#info"); + + char *client_name = weechat_string_eval_expression( + "weechat ${info:version}", NULL, NULL, NULL); + char **serial = weechat_string_dyn_alloc(256); + weechat_string_dyn_concat(serial, "client/pc//", -1); + weechat_string_dyn_concat(serial, client_name, -1); + weechat_string_dyn_concat(serial, "<", -1); + + xmpp_stanza_t *identity = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(identity, "identity"); + xmpp_stanza_set_attribute(identity, "category", "client"); + xmpp_stanza_set_attribute(identity, "name", client_name); + free(client_name); + xmpp_stanza_set_attribute(identity, "type", "pc"); + xmpp_stanza_add_child(query, identity); + xmpp_stanza_release(identity); + + xmpp_stanza_t *feature = NULL; + +#define FEATURE(NS) \ + feature = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_name(feature, "feature"); \ + xmpp_stanza_set_attribute(feature, "var", NS); \ + xmpp_stanza_add_child(query, feature); \ + xmpp_stanza_release(feature); \ + weechat_string_dyn_concat(serial, NS, -1); \ + weechat_string_dyn_concat(serial, "<", -1); + + FEATURE("eu.siacs.conversations.axolotl.devicelist+notify"); + FEATURE("http://jabber.org/protocol/caps"); + FEATURE("http://jabber.org/protocol/chatstates"); + FEATURE("http://jabber.org/protocol/disco#info"); + FEATURE("http://jabber.org/protocol/disco#items"); + FEATURE("http://jabber.org/protocol/muc"); + FEATURE("http://jabber.org/protocol/nick+notify"); + FEATURE("jabber:iq:version"); + FEATURE("jabber:x:conference"); + FEATURE("jabber:x:oob"); + FEATURE("storage:bookmarks+notify"); + FEATURE("urn:xmpp:avatar:metadata+notify"); + FEATURE("urn:xmpp:chat-markers:0"); + FEATURE("urn:xmpp:idle:1"); + //FEATURE("urn:xmpp:jingle-message:0"); + //FEATURE("urn:xmpp:jingle:1"); + //FEATURE("urn:xmpp:jingle:apps:dtls:0"); + //FEATURE("urn:xmpp:jingle:apps:file-transfer:3"); + //FEATURE("urn:xmpp:jingle:apps:file-transfer:4"); + //FEATURE("urn:xmpp:jingle:apps:file-transfer:5"); + //FEATURE("urn:xmpp:jingle:apps:rtp:1"); + //FEATURE("urn:xmpp:jingle:apps:rtp:audio"); + //FEATURE("urn:xmpp:jingle:apps:rtp:video"); + //FEATURE("urn:xmpp:jingle:jet-omemo:0"); + //FEATURE("urn:xmpp:jingle:jet:0"); + //FEATURE("urn:xmpp:jingle:transports:ibb:1"); + //FEATURE("urn:xmpp:jingle:transports:ice-udp:1"); + //FEATURE("urn:xmpp:jingle:transports:s5b:1"); + FEATURE("urn:xmpp:message-correct:0"); + FEATURE("urn:xmpp:ping"); + FEATURE("urn:xmpp:receipts"); + FEATURE("urn:xmpp:time"); +#undef FEATURE + + xmpp_stanza_t *x = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(x, "x"); + xmpp_stanza_set_ns(x, "jabber:x:data"); + xmpp_stanza_set_attribute(x, "type", "result"); + + static struct utsname osinfo; + if (uname(&osinfo) < 0) + { + *osinfo.sysname = 0; + *osinfo.release = 0; + } + + xmpp_stanza_t *field, *value, *text; + // This is utter bullshit, TODO: anything but this. +#define FEATURE1(VAR, TYPE, VALUE) \ + field = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_name(field, "field"); \ + xmpp_stanza_set_attribute(field, "var", VAR); \ + if(TYPE) xmpp_stanza_set_attribute(field, "type", TYPE); \ + value = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_name(value, "value"); \ + text = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_text(text, VALUE); \ + xmpp_stanza_add_child(value, text); \ + xmpp_stanza_release(text); \ + xmpp_stanza_add_child(field, value); \ + xmpp_stanza_release(value); \ + xmpp_stanza_add_child(x, field); \ + xmpp_stanza_release(field); \ + if (strcmp(VAR, "FORM_TYPE") == 0) { \ + weechat_string_dyn_concat(serial, VAR, -1); \ + weechat_string_dyn_concat(serial, "<", -1); \ + } \ + weechat_string_dyn_concat(serial, VALUE, -1); \ + weechat_string_dyn_concat(serial, "<", -1); +#define FEATURE2(VAR, TYPE, VALUE1, VALUE2) \ + field = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_name(field, "field"); \ + xmpp_stanza_set_attribute(field, "var", VAR); \ + xmpp_stanza_set_attribute(field, "type", TYPE); \ + value = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_name(value, "value"); \ + text = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_text(text, VALUE1); \ + xmpp_stanza_add_child(value, text); \ + xmpp_stanza_release(text); \ + xmpp_stanza_add_child(field, value); \ + xmpp_stanza_release(value); \ + value = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_name(value, "value"); \ + text = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_text(text, VALUE2); \ + xmpp_stanza_add_child(value, text); \ + xmpp_stanza_release(text); \ + xmpp_stanza_add_child(field, value); \ + xmpp_stanza_release(value); \ + xmpp_stanza_add_child(x, field); \ + xmpp_stanza_release(field); \ + weechat_string_dyn_concat(serial, VAR, -1); \ + weechat_string_dyn_concat(serial, "<", -1); \ + weechat_string_dyn_concat(serial, VALUE1, -1); \ + weechat_string_dyn_concat(serial, "<", -1); \ + weechat_string_dyn_concat(serial, VALUE2, -1); \ + weechat_string_dyn_concat(serial, "<", -1); + + FEATURE1("FORM_TYPE", "hidden", "urn:xmpp:dataforms:softwareinfo"); + FEATURE2("ip_version", "text-multi", "ipv4", "ipv6"); + FEATURE1("os", NULL, osinfo.sysname); + FEATURE1("os_version", NULL, osinfo.release); + FEATURE1("software", NULL, "weechat"); + FEATURE1("software_version", NULL, weechat_info_get("version", NULL)); +#undef FEATURE1 +#undef FEATURE2 + + xmpp_stanza_add_child(query, x); + xmpp_stanza_release(x); + + xmpp_stanza_set_type(reply, "result"); + xmpp_stanza_add_child(reply, query); + + weechat_printf(NULL, "verstr: %s", *serial); + unsigned char digest[20]; + xmpp_sha1_t *sha1 = xmpp_sha1_new(account->context); + xmpp_sha1_update(sha1, (unsigned char*)*serial, strlen(*serial)); + xmpp_sha1_final(sha1); + weechat_string_dyn_free(serial, 1); + xmpp_sha1_to_digest(sha1, digest); + xmpp_sha1_free(sha1); + + if (hash) + { + char *cap_hash = xmpp_base64_encode(account->context, digest, 20); + *hash = strdup(cap_hash); + xmpp_free(account->context, cap_hash); + weechat_printf(NULL, "verhash: %s", *hash); + } + + return reply; +} + int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { struct t_account *account = (struct t_account *)userdata; - xmpp_stanza_t *reply, *query, *identity, *feature, *x, *field, *value, *text, *fin; + xmpp_stanza_t *reply, *query, *text, *fin; xmpp_stanza_t *pubsub, *items, *item, *list, *bundle, *device; xmpp_stanza_t *storage, *conference, *nick; - static struct utsname osinfo; const char *id = xmpp_stanza_get_id(stanza); const char *from = xmpp_stanza_get_from(stanza); + const char *to = xmpp_stanza_get_to(stanza); query = xmpp_stanza_get_child_by_name_and_ns( stanza, "query", "http://jabber.org/protocol/disco#info"); const char *type = xmpp_stanza_get_attribute(stanza, "type"); - if (query && type && weechat_strcasecmp(type, "get") == 0) + if (query && type) { - char *client_name; - - reply = xmpp_stanza_reply(stanza); - xmpp_stanza_set_type(reply, "result"); - - client_name = weechat_string_eval_expression("weechat ${info:version}", - NULL, NULL, NULL); - - identity = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(identity, "identity"); - xmpp_stanza_set_attribute(identity, "category", "client"); - xmpp_stanza_set_attribute(identity, "name", client_name); - xmpp_stanza_set_attribute(identity, "type", "pc"); - xmpp_stanza_add_child(query, identity); - xmpp_stanza_release(identity); - -#define FEATURE(ns) \ - feature = xmpp_stanza_new(account->context); \ - xmpp_stanza_set_name(feature, "feature"); \ - xmpp_stanza_set_attribute(feature, "var", ns); \ - xmpp_stanza_add_child(query, feature); \ - xmpp_stanza_release(feature); - - FEATURE("eu.siacs.conversations.axolotl.devicelist+notify"); - FEATURE("http://jabber.org/protocol/caps"); - FEATURE("http://jabber.org/protocol/chatstates"); - FEATURE("http://jabber.org/protocol/disco#info"); - FEATURE("http://jabber.org/protocol/disco#items"); - FEATURE("http://jabber.org/protocol/muc"); - FEATURE("http://jabber.org/protocol/nick+notify"); - FEATURE("jabber:iq:version"); - FEATURE("jabber:x:conference"); - FEATURE("jabber:x:oob"); - FEATURE("storage:bookmarks+notify"); - FEATURE("urn:xmpp:avatar:metadata+notify"); - FEATURE("urn:xmpp:chat-markers:0"); - FEATURE("urn:xmpp:idle:1"); - //FEATURE("urn:xmpp:jingle-message:0"); - //FEATURE("urn:xmpp:jingle:1"); - //FEATURE("urn:xmpp:jingle:apps:dtls:0"); - //FEATURE("urn:xmpp:jingle:apps:file-transfer:3"); - //FEATURE("urn:xmpp:jingle:apps:file-transfer:4"); - //FEATURE("urn:xmpp:jingle:apps:file-transfer:5"); - //FEATURE("urn:xmpp:jingle:apps:rtp:1"); - //FEATURE("urn:xmpp:jingle:apps:rtp:audio"); - //FEATURE("urn:xmpp:jingle:apps:rtp:video"); - //FEATURE("urn:xmpp:jingle:jet-omemo:0"); - //FEATURE("urn:xmpp:jingle:jet:0"); - //FEATURE("urn:xmpp:jingle:transports:ibb:1"); - //FEATURE("urn:xmpp:jingle:transports:ice-udp:1"); - //FEATURE("urn:xmpp:jingle:transports:s5b:1"); - FEATURE("urn:xmpp:message-correct:0"); - FEATURE("urn:xmpp:ping"); - FEATURE("urn:xmpp:receipts"); - FEATURE("urn:xmpp:time"); -#undef FEATURE - - x = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(x, "x"); - xmpp_stanza_set_ns(x, "jabber:x:data"); - xmpp_stanza_set_attribute(x, "type", "result"); - - if (uname(&osinfo) < 0) - { - *osinfo.sysname = 0; - *osinfo.release = 0; - } - - // This is utter bullshit, TODO: anything but this. + if (weechat_strcasecmp(type, "get") == 0) { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "FORM_TYPE"); - xmpp_stanza_set_attribute(field, "type", "hidden"); - - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + reply = connection__get_caps(xmpp_stanza_reply(stanza), account, NULL); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "urn:xmpp:dataforms:softwareinfo"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); - - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); - - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); + xmpp_send(conn, reply); + xmpp_stanza_release(reply); } + if (weechat_strcasecmp(type, "result") == 0) { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "ip_version"); - xmpp_stanza_set_attribute(field, "type", "text-multi"); - - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); - - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "ipv4"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); - - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); - - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); - - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "ipv6"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); - - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); - - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); } - - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "os"); - - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); - - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, osinfo.sysname); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); - - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); - - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } - - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "os_version"); - - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); - - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, osinfo.release); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); - - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); - - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } - - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "software"); - - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); - - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "weechat"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); - - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); - - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } - - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "software_version"); - - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); - - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, weechat_info_get("version", NULL)); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); - - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); - - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } - - xmpp_stanza_add_child(query, x); - xmpp_stanza_release(x); - - xmpp_stanza_add_child(reply, query); - - xmpp_send(conn, reply); - xmpp_stanza_release(reply); - - free(client_name); } pubsub = xmpp_stanza_get_child_by_name_and_ns( @@ -926,14 +960,47 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd { item = xmpp_stanza_get_child_by_name(items, "item"); if (item) - item_id = xmpp_stanza_get_id(item); - if (item && item_id && weechat_strcasecmp(item_id, "current") == 0) { + item_id = xmpp_stanza_get_id(item); list = xmpp_stanza_get_child_by_name_and_ns( item, "list", "eu.siacs.conversations.axolotl"); if (list && account->omemo) { - omemo__handle_devicelist(account->omemo, from, items); + omemo__handle_devicelist(account->omemo, + from ? from : account_jid(account), items); + + xmpp_stanza_t *children[2] = {NULL}; + for (device = xmpp_stanza_get_children(list); + device; device = xmpp_stanza_get_next(device)) + { + const char *name = xmpp_stanza_get_name(device); + if (weechat_strcasecmp(name, "device") != 0) + continue; + + const char *device_id = xmpp_stanza_get_id(device); + + char bundle_node[128] = {0}; + snprintf(bundle_node, sizeof(bundle_node), + "eu.siacs.conversations.axolotl.bundles:%s", + device_id); + + children[1] = NULL; + children[0] = + stanza__iq_pubsub_items(account->context, NULL, + strdup(bundle_node)); + children[0] = + stanza__iq_pubsub(account->context, NULL, children, + with_noop("http://jabber.org/protocol/pubsub")); + char *uuid = xmpp_uuid_gen(account->context); + children[0] = + stanza__iq(account->context, NULL, children, NULL, uuid, + to ? strdup(to) : NULL, from ? strdup(from) : NULL, + strdup("get")); + xmpp_free(account->context, uuid); + + xmpp_send(conn, children[0]); + xmpp_stanza_release(children[0]); + } if (weechat_strcasecmp(account_jid(account), from) == 0) { @@ -975,6 +1042,11 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd } reply = account__get_devicelist(account); + char *uuid = xmpp_uuid_gen(account->context); + xmpp_stanza_set_id(reply, uuid); + xmpp_free(account->context, uuid); + xmpp_stanza_set_attribute(reply, "to", from); + xmpp_stanza_set_attribute(reply, "from", to); xmpp_send(conn, reply); xmpp_stanza_release(reply); } @@ -997,7 +1069,8 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd strlen("eu.siacs.conversations.axolotl.bundles:"); if (account->omemo && strlen(items_node) > node_prefix) { - omemo__handle_bundle(account->omemo, from, + omemo__handle_bundle(account->omemo, + from ? from : account_jid(account), strtol(items_node+node_prefix, NULL, 10), items); @@ -1046,6 +1119,12 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd weechat_string_dyn_concat(command, intext, -1); } weechat_command(account->buffer, *command); + struct t_channel *ptr_channel = + channel__search(account, jid); + struct t_gui_buffer *ptr_buffer = + ptr_channel ? ptr_channel->buffer : NULL; + if (ptr_buffer) + weechat_buffer_set(ptr_buffer, "short_name", name); weechat_string_dyn_free(command, 1); } @@ -1108,7 +1187,6 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, xmpp_stanza_t *pres, *pres__c, *pres__status, *pres__status__text, *pres__x, *pres__x__text, **children; - char cap_hash[28+1] = {0}; xmpp_handler_add(conn, &connection__version_handler, "jabber:iq:version", "iq", NULL, account); @@ -1135,8 +1213,15 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, xmpp_stanza_set_ns(pres__c, "http://jabber.org/protocol/caps"); xmpp_stanza_set_attribute(pres__c, "hash", "sha-1"); xmpp_stanza_set_attribute(pres__c, "node", "http://weechat.org"); - snprintf(cap_hash, sizeof(cap_hash), "%027ld=", time(NULL)); + + xmpp_stanza_t *caps = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(caps, "caps"); + char *cap_hash; + caps = connection__get_caps(caps, account, &cap_hash); + xmpp_stanza_release(caps); xmpp_stanza_set_attribute(pres__c, "ver", cap_hash); + free(cap_hash); + children[0] = pres__c; pres__status = xmpp_stanza_new(account->context); @@ -1206,21 +1291,26 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, children[0] = stanza__iq_pubsub(account->context, NULL, children, with_noop("http://jabber.org/protocol/pubsub")); + char *uuid = xmpp_uuid_gen(account->context); children[0] = - stanza__iq(account->context, NULL, children, NULL, strdup("fetch1"), + stanza__iq(account->context, NULL, children, NULL, uuid, strdup(account_jid(account)), strdup(account_jid(account)), strdup("get")); + xmpp_free(account->context, uuid); xmpp_send(conn, children[0]); xmpp_stanza_release(children[0]); omemo__init(account->buffer, &account->omemo, account->name); - children[0] = - omemo__get_bundle(account->context, - strdup(account_jid(account)), NULL, account->omemo); - xmpp_send(conn, children[0]); - xmpp_stanza_release(children[0]); + if (account->omemo) + { + children[0] = + omemo__get_bundle(account->context, + strdup(account_jid(account)), NULL, account->omemo); + xmpp_send(conn, children[0]); + xmpp_stanza_release(children[0]); + } (void) weechat_hook_signal_send("xmpp_account_connected", WEECHAT_HOOK_SIGNAL_STRING, account->name); @@ -1235,7 +1325,6 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, char* connection__rand_string(int length) { char *string = malloc(length); - srand(time(NULL)); for(int i = 0; i < length; ++i){ string[i] = '0' + rand()%72; // starting on '0', ending on '}' if (!((string[i] >= '0' && string[i] <= '9') || diff --git a/input.c b/input.c index 6df232e..d51b3e4 100644 --- a/input.c +++ b/input.c @@ -34,16 +34,20 @@ int input__data(struct t_gui_buffer *buffer, const char *text) return WEECHAT_RC_OK; } - channel__send_message(account, channel, channel->id, text); + if (channel__send_message(account, channel, channel->id, text) == WEECHAT_RC_OK) + return WEECHAT_RC_OK; + else + { + return WEECHAT_RC_OK_EAT; + } } else { weechat_printf(buffer, _("%s%s: this buffer is not a channel!"), weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return WEECHAT_RC_OK; } - - return WEECHAT_RC_OK; } int input__data_cb(const void *pointer, void *data, diff --git a/libomemo.scm b/libomemo.scm index 28e141a..b9c7139 100644 --- a/libomemo.scm +++ b/libomemo.scm @@ -1,4 +1,4 @@ -(define-module (gnu packages messaging) +(define-module (libomemo) #:use-module (gnu packages) #:use-module (gnu packages admin) #:use-module (gnu packages aidc) diff --git a/omemo.c b/omemo.c index 1c30d63..7a9a32c 100644 --- a/omemo.c +++ b/omemo.c @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +25,11 @@ struct t_omemo_db { MDB_dbi dbi_omemo; }; +struct t_pre_key { + const char *id; + const char *public_key; +}; + #include "plugin.h" #include "xmpp/stanza.h" #include "account.h" @@ -51,13 +58,13 @@ const char *OMEMO_ADVICE = "[OMEMO encrypted message (XEP-0384)]"; size_t base64_decode(const char *buffer, size_t length, uint8_t **result) { - *result = malloc(sizeof(uint8_t) * length); + *result = calloc(length + 1, sizeof(uint8_t)); return weechat_string_base_decode(64, buffer, (char*)*result); } size_t base64_encode(const uint8_t *buffer, size_t length, char **result) { - *result = malloc(sizeof(char) * (length * 2)); + *result = calloc(length * 2, sizeof(char)); return weechat_string_base_encode(64, (char*)buffer, length, *result); } @@ -66,15 +73,17 @@ int aes_decrypt(const uint8_t *ciphertext, size_t ciphertext_len, uint8_t **plaintext, size_t *plaintext_len) { gcry_cipher_hd_t cipher = NULL; - if (gcry_cipher_open(&cipher, GCRY_CIPHER_AES192, + if (gcry_cipher_open(&cipher, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE)) goto cleanup; if (gcry_cipher_setkey(cipher, key, AES_KEY_SIZE)) goto cleanup; if (gcry_cipher_setiv(cipher, iv, AES_IV_SIZE)) goto cleanup; - *plaintext_len = ciphertext_len; + *plaintext_len = 0; *plaintext = malloc((sizeof(uint8_t) * *plaintext_len) + 1); if (gcry_cipher_decrypt(cipher, *plaintext, *plaintext_len, ciphertext, ciphertext_len)) goto cleanup; + *plaintext_len = ciphertext_len; if (gcry_cipher_checktag(cipher, tag, tag_len)) goto cleanup; + gcry_cipher_close(cipher); return 1; cleanup: gcry_cipher_close(cipher); @@ -91,15 +100,17 @@ int aes_encrypt(const uint8_t *plaintext, size_t plaintext_len, *key = gcry_random_bytes(AES_KEY_SIZE + *tag_len, GCRY_STRONG_RANDOM); gcry_cipher_hd_t cipher = NULL; - if (gcry_cipher_open(&cipher, GCRY_CIPHER_AES192, + if (gcry_cipher_open(&cipher, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE)) goto cleanup; if (gcry_cipher_setkey(cipher, key, AES_KEY_SIZE)) goto cleanup; if (gcry_cipher_setiv(cipher, iv, AES_IV_SIZE)) goto cleanup; - *ciphertext_len = plaintext_len; + *ciphertext_len = 0; *ciphertext = malloc((sizeof(uint8_t) * *ciphertext_len) + 1); if (gcry_cipher_encrypt(cipher, *ciphertext, *ciphertext_len, plaintext, plaintext_len)) goto cleanup; + *ciphertext_len = plaintext_len; if (gcry_cipher_gettag(cipher, *tag, *tag_len)) goto cleanup; + gcry_cipher_close(cipher); return 1; cleanup: gcry_cipher_close(cipher); @@ -477,15 +488,16 @@ no_error: int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_local_private_key = mdb_val_str("local_private_key"); MDB_val k_local_public_key = mdb_val_str("local_public_key"); MDB_val v_local_private_key, v_local_public_key; - if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "iks_get_identity_key_pair\n"); + if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - goto cleanup; + return -1; } if (!mdb_get(transaction, omemo->db->dbi_omemo, @@ -496,6 +508,7 @@ int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **priva *private_data = signal_buffer_create(v_local_private_key.mv_data, v_local_private_key.mv_size); *public_data = signal_buffer_create(v_local_public_key.mv_data, v_local_public_key.mv_size); + weechat_printf(NULL, "-iks_get_identity_key_pair\n"); if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); @@ -519,8 +532,17 @@ int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **priva v_local_public_key.mv_data = signal_buffer_data(*public_data); v_local_public_key.mv_size = signal_buffer_len(*public_data); + weechat_printf(NULL, "-iks_get_identity_key_pair\n"); + mdb_txn_abort(transaction); + weechat_printf(NULL, "iks_gen_identity_key_pair\n"); + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + return -1; + } + if (mdb_put(transaction, omemo->db->dbi_omemo, - &k_local_private_key, &v_local_private_key, MDB_NOOVERWRITE) && + &k_local_private_key, &v_local_private_key, MDB_NOOVERWRITE) || mdb_put(transaction, omemo->db->dbi_omemo, &k_local_public_key, &v_local_public_key, MDB_NOOVERWRITE)) { @@ -529,6 +551,7 @@ int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **priva goto cleanup; }; + weechat_printf(NULL, "-iks_gen_identity_key_pair\n"); if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); @@ -539,10 +562,20 @@ int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **priva v_local_private_key.mv_size); *public_data = signal_buffer_create(v_local_public_key.mv_data, v_local_public_key.mv_size); + omemo->identity = identity; } + unsigned char digest[20] = {0}; + xmpp_sha1_digest((const unsigned char *)v_local_public_key.mv_data, + v_local_public_key.mv_size, digest); + char *data = NULL; + base64_encode(digest, 20 * sizeof(unsigned char), &data); + weechat_printf(NULL, "%somemo iks dump: %s", + weechat_prefix("error"), data); + return 0; cleanup: + weechat_printf(NULL, "-iks_get_identity_key_pair\n"); mdb_txn_abort(transaction); return -1; } @@ -550,15 +583,16 @@ cleanup: int iks_get_local_registration_id(void *user_data, uint32_t *registration_id) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_local_registration_id = mdb_val_str("local_registration_id"); MDB_val v_local_registration_id = mdb_val_sizeof(uint32_t); // Return the local client's registration ID - if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "iks_get_local_registration_id\n"); + if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - goto cleanup; + return -1; } if (!mdb_get(transaction, omemo->db->dbi_omemo, @@ -567,6 +601,7 @@ int iks_get_local_registration_id(void *user_data, uint32_t *registration_id) { *registration_id = *(uint32_t*)v_local_registration_id.mv_data; + weechat_printf(NULL, "-iks_get_local_registration_id\n"); if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to read lmdb transaction", weechat_prefix("error")); @@ -580,6 +615,15 @@ int iks_get_local_registration_id(void *user_data, uint32_t *registration_id) &generated_id, 0, omemo->context); v_local_registration_id.mv_data = &generated_id; + weechat_printf(NULL, "-iks_get_local_registration_id\n"); + mdb_txn_abort(transaction); + weechat_printf(NULL, "iks_get_local_registration_id\n"); + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + return -1; + } + if (mdb_put(transaction, omemo->db->dbi_omemo, &k_local_registration_id, &v_local_registration_id, MDB_NOOVERWRITE)) @@ -589,6 +633,7 @@ int iks_get_local_registration_id(void *user_data, uint32_t *registration_id) goto cleanup; }; + weechat_printf(NULL, "-iks_get_local_registration_id\n"); if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); @@ -600,6 +645,7 @@ int iks_get_local_registration_id(void *user_data, uint32_t *registration_id) return 0; cleanup: + weechat_printf(NULL, "-iks_get_local_registration_id\n"); mdb_txn_abort(transaction); return -1; } @@ -607,104 +653,94 @@ cleanup: int iks_save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; - MDB_val k_registration_id = { - .mv_data = NULL, - .mv_size = strlen("registration_id_") + address->name_len, - }; - MDB_val v_registration_id = mdb_val_intptr((uint32_t*)&address->device_id); + MDB_txn *transaction = NULL; MDB_val k_identity_key = { .mv_data = NULL, - .mv_size = strlen("identity_key_") + address->name_len, + .mv_size = strlen("identity_key_") + address->name_len + + 1 + 10, }; MDB_val v_identity_key = {.mv_data = key_data, .mv_size = key_len}; - k_registration_id.mv_data = malloc(sizeof(char) * ( - k_registration_id.mv_size + 1)); - snprintf(k_registration_id.mv_data, k_registration_id.mv_size, - "registration_id_%s", address->name); k_identity_key.mv_data = malloc(sizeof(char) * ( k_identity_key.mv_size + 1)); - snprintf(k_identity_key.mv_data, k_identity_key.mv_size, - "identity_key_%s", address->name); + k_identity_key.mv_size = + snprintf(k_identity_key.mv_data, k_identity_key.mv_size + 1, + "identity_key_%s_%u", address->name, address->device_id); + weechat_printf(NULL, "iks_save_identity\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); return -1; } - if (mdb_put(transaction, omemo->db->dbi_omemo, &k_registration_id, - &v_registration_id, 0) || - mdb_put(transaction, omemo->db->dbi_omemo, &k_identity_key, + if (mdb_put(transaction, omemo->db->dbi_omemo, &k_identity_key, &v_identity_key, 0)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; } int iks_is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; - MDB_val k_registration_id = { - .mv_data = NULL, - .mv_size = strlen("registration_id_") + address->name_len, - }; - MDB_val v_registration_id = mdb_val_intptr((uint32_t*)&address->device_id); + MDB_txn *transaction = NULL; MDB_val k_identity_key = { .mv_data = NULL, - .mv_size = strlen("identity_key_") + address->name_len, + .mv_size = strlen("identity_key_") + address->name_len + + 1 + 10, }; MDB_val v_identity_key = {.mv_data = key_data, .mv_size = key_len}; int trusted = 1; - k_registration_id.mv_data = malloc(sizeof(char) * ( - k_registration_id.mv_size + 1)); - snprintf(k_registration_id.mv_data, k_registration_id.mv_size, - "registration_id_%s", address->name); k_identity_key.mv_data = malloc(sizeof(char) * ( k_identity_key.mv_size + 1)); - snprintf(k_identity_key.mv_data, k_identity_key.mv_size, - "identity_key_%s", address->name); + k_identity_key.mv_size = + snprintf(k_identity_key.mv_data, k_identity_key.mv_size + 1, + "identity_key_%s_%u", address->name, address->device_id); + weechat_printf(NULL, "iks_is_trusted_identity\n"); if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); return -1; } - if (mdb_get(transaction, omemo->db->dbi_omemo, &k_registration_id, - &v_registration_id) || - mdb_get(transaction, omemo->db->dbi_omemo, &k_identity_key, + if (mdb_get(transaction, omemo->db->dbi_omemo, &k_identity_key, &v_identity_key)) { weechat_printf(NULL, "%sxmpp: failed to read lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; - if (*(uint32_t*)v_registration_id.mv_data != (uint32_t)address->device_id) - trusted = 0; if (v_identity_key.mv_size != key_len || memcmp(v_identity_key.mv_data, key_data, key_len) != 0) trusted = 0; + weechat_printf(NULL, "-iks_is_trusted_identity\n"); if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; - return trusted; + return 1 | trusted; +cleanup: + weechat_printf(NULL, "-iks_is_trusted_identity\n"); + mdb_txn_abort(transaction); + return -1; } void iks_destroy_func(void *user_data) @@ -717,7 +753,7 @@ void iks_destroy_func(void *user_data) int pks_store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_pre_key = { .mv_data = NULL, .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) @@ -727,9 +763,10 @@ int pks_store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, v k_pre_key.mv_data = malloc(sizeof(char) * ( k_pre_key.mv_size + 1)); k_pre_key.mv_size = - snprintf(k_pre_key.mv_data, k_pre_key.mv_size, + snprintf(k_pre_key.mv_data, k_pre_key.mv_size + 1, "pre_key_%-10u", pre_key_id); + weechat_printf(NULL, "pks_store_pre_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -740,22 +777,25 @@ int pks_store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, v &v_pre_key, 0)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; } int pks_contains_pre_key(uint32_t pre_key_id, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_pre_key = { .mv_data = NULL, .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) @@ -765,13 +805,14 @@ int pks_contains_pre_key(uint32_t pre_key_id, void *user_data) k_pre_key.mv_data = malloc(sizeof(char) * ( k_pre_key.mv_size + 1)); k_pre_key.mv_size = - snprintf(k_pre_key.mv_data, k_pre_key.mv_size, + snprintf(k_pre_key.mv_data, k_pre_key.mv_size + 1, "pre_key_%-10u", pre_key_id); + weechat_printf(NULL, "pks_contains_pre_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - return 0; + return -1; } if (mdb_get(transaction, omemo->db->dbi_omemo, &k_pre_key, @@ -779,28 +820,32 @@ int pks_contains_pre_key(uint32_t pre_key_id, void *user_data) weechat_printf(NULL, "%sxmpp: failed to read lmdb value", weechat_prefix("error")); mdb_txn_abort(transaction); - return 0; + goto cleanup; }; mdb_txn_abort(transaction); return 1; +cleanup: + mdb_txn_abort(transaction); + return 0; } uint32_t pks_get_count(struct t_omemo *omemo, int increment) { uint32_t count = PRE_KEY_START; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_pre_key_idx = { .mv_data = "pre_key_idx", .mv_size = strlen("pre_key_idx"), }; MDB_val v_pre_key_idx = mdb_val_intptr(&count); + weechat_printf(NULL, "pks_get_count\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - goto cleanup; + return -1; } if (!mdb_get(transaction, omemo->db->dbi_omemo, @@ -833,7 +878,7 @@ cleanup: int pks_load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_pre_key = { .mv_data = NULL, .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) @@ -843,13 +888,14 @@ int pks_load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_dat k_pre_key.mv_data = malloc(sizeof(char) * ( k_pre_key.mv_size + 1)); k_pre_key.mv_size = - snprintf(k_pre_key.mv_data, k_pre_key.mv_size, + snprintf(k_pre_key.mv_data, k_pre_key.mv_size + 1, "pre_key_%-10u", pre_key_id); + weechat_printf(NULL, "pks_load_pre_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - goto cleanup; + return -1; } if (!mdb_get(transaction, omemo->db->dbi_omemo, @@ -893,7 +939,7 @@ cleanup: int pks_remove_pre_key(uint32_t pre_key_id, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_pre_key = { .mv_data = NULL, .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) @@ -903,9 +949,10 @@ int pks_remove_pre_key(uint32_t pre_key_id, void *user_data) k_pre_key.mv_data = malloc(sizeof(char) * ( k_pre_key.mv_size + 1)); k_pre_key.mv_size = - snprintf(k_pre_key.mv_data, k_pre_key.mv_size, + snprintf(k_pre_key.mv_data, k_pre_key.mv_size + 1, "pre_key_%-10u", pre_key_id); + weechat_printf(NULL, "pks_remove_pre_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -916,16 +963,19 @@ int pks_remove_pre_key(uint32_t pre_key_id, void *user_data) &v_pre_key)) { weechat_printf(NULL, "%sxmpp: failed to erase lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; } void pks_destroy_func(void *user_data) @@ -938,7 +988,7 @@ void pks_destroy_func(void *user_data) int spks_load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_signed_pre_key = { .mv_data = NULL, .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) @@ -948,13 +998,14 @@ int spks_load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, k_signed_pre_key.mv_data = malloc(sizeof(char) * ( k_signed_pre_key.mv_size + 1)); k_signed_pre_key.mv_size = - snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size, + snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size + 1, "signed_pre_key_%-10u", signed_pre_key_id); + weechat_printf(NULL, "spks_load_signed_pre_key %s\n", k_signed_pre_key.mv_data); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - goto cleanup; + return -1; } if (!mdb_get(transaction, omemo->db->dbi_omemo, @@ -996,6 +1047,39 @@ int spks_load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, *record = serialized_key; } + unsigned char digest[20] = {0}; + xmpp_sha1_digest((const unsigned char *)signal_buffer_data(*record), + signal_buffer_len(*record), digest); + char *data = NULL; + base64_encode(digest, 20 * sizeof(unsigned char), &data); + weechat_printf(NULL, "%somemo spks dump: %s", + weechat_prefix("error"), data); + + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction) || + mdb_get(transaction, omemo->db->dbi_omemo, + &k_signed_pre_key, &v_signed_pre_key) || + mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed somehow", + weechat_prefix("error")); + goto cleanup; + }; + session_signed_pre_key *signed_pre_key_de = NULL; + session_signed_pre_key_deserialize(&signed_pre_key_de, + v_signed_pre_key.mv_data, v_signed_pre_key.mv_size, + omemo->context); + ec_key_pair *signed_key_pair = session_signed_pre_key_get_key_pair(signed_pre_key_de); + ec_public_key *signed_public_key = ec_key_pair_get_public(signed_key_pair); + signal_buffer *signed_key_buf = NULL; + ec_public_key_serialize(&signed_key_buf, signed_public_key); + const uint8_t *signature_buf = session_signed_pre_key_get_signature(signed_pre_key_de); + size_t signature_len = session_signed_pre_key_get_signature_len(signed_pre_key_de); + int valid = curve_verify_signature(ratchet_identity_key_pair_get_public(omemo->identity), + signal_buffer_data(signed_key_buf), signal_buffer_len(signed_key_buf), + signature_buf, signature_len); + weechat_printf(NULL, "%somemo: %s ED25519 identity signature", + weechat_prefix(valid <= 0 ? "error": "network"), + valid <= 0 ? "failed to validate" : "succesfully validated"); + return 0; cleanup: mdb_txn_abort(transaction); @@ -1005,7 +1089,7 @@ cleanup: int spks_store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_signed_pre_key = { .mv_data = NULL, .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) @@ -1015,9 +1099,10 @@ int spks_store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_ k_signed_pre_key.mv_data = malloc(sizeof(char) * ( k_signed_pre_key.mv_size + 1)); k_signed_pre_key.mv_size = - snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size, + snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size + 1, "signed_pre_key_%-10u", signed_pre_key_id); + weechat_printf(NULL, "spks_store_signed_pre_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -1028,22 +1113,25 @@ int spks_store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_ &v_signed_pre_key, 0)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; } int spks_contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_signed_pre_key = { .mv_data = NULL, .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) @@ -1053,30 +1141,34 @@ int spks_contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) k_signed_pre_key.mv_data = malloc(sizeof(char) * ( k_signed_pre_key.mv_size + 1)); k_signed_pre_key.mv_size = - snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size, + snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size + 1, "signed_pre_key_%-10u", signed_pre_key_id); + weechat_printf(NULL, "spks_contains_signed_pre_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - return 0; + return -1; } if (mdb_get(transaction, omemo->db->dbi_omemo, &k_signed_pre_key, &v_signed_pre_key)) { mdb_txn_abort(transaction); - return 0; + goto cleanup; }; mdb_txn_abort(transaction); return 1; +cleanup: + mdb_txn_abort(transaction); + return 0; } int spks_remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_signed_pre_key = { .mv_data = NULL, .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) @@ -1086,9 +1178,10 @@ int spks_remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) k_signed_pre_key.mv_data = malloc(sizeof(char) * ( k_signed_pre_key.mv_size + 1)); k_signed_pre_key.mv_size = - snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size, + snprintf(k_signed_pre_key.mv_data, k_signed_pre_key.mv_size + 1, "signed_pre_key_%-10u", signed_pre_key_id); + weechat_printf(NULL, "spks_remove_signed_pre_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -1099,16 +1192,19 @@ int spks_remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) &v_signed_pre_key)) { weechat_printf(NULL, "%sxmpp: failed to erase lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; } void spks_destroy_func(void *user_data) @@ -1121,7 +1217,7 @@ void spks_destroy_func(void *user_data) int ss_load_session_func(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_session = { .mv_data = NULL, .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + @@ -1133,63 +1229,46 @@ int ss_load_session_func(signal_buffer **record, signal_buffer **user_record, co .mv_size = strlen("user_") + 10 + //strlen(address->device_id) + 1 + strlen(address->name), }; - MDB_val v_user; + MDB_val v_user; (void) v_user; (void) user_record; - k_session.mv_data = malloc(sizeof(char) * ( - k_session.mv_size + 1)); + k_session.mv_data = malloc(sizeof(char) * (k_session.mv_size + 1)); k_session.mv_size = - snprintf(k_session.mv_data, k_session.mv_size, + snprintf(k_session.mv_data, k_session.mv_size + 1, "session_%u_%s", address->device_id, address->name); - k_user.mv_data = malloc(sizeof(char) * ( - k_user.mv_size + 1)); + k_user.mv_data = malloc(sizeof(char) * (k_user.mv_size + 1)); k_user.mv_size = - snprintf(k_user.mv_data, k_user.mv_size, + snprintf(k_user.mv_data, k_user.mv_size + 1, "user_%u_%s", address->device_id, address->name); - if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "ss_load_session_func\n"); + if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - goto cleanup; + return -1; } - if (!mdb_get(transaction, omemo->db->dbi_omemo, - &k_session, &v_session)/* && - !mdb_get(transaction, omemo->db->dbi_omemo, + if (mdb_get(transaction, omemo->db->dbi_omemo, + &k_session, &v_session)/* || + mdb_get(transaction, omemo->db->dbi_omemo, &k_user, &v_user)*/) { - *record = signal_buffer_create(v_session.mv_data, v_session.mv_size); - //*user_record = signal_buffer_create(v_user.mv_data, v_user.mv_size); - - if (mdb_txn_commit(transaction)) { - weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", - weechat_prefix("error")); - goto cleanup; - }; + mdb_txn_abort(transaction); + return 0; } - else - { - /* - v_session.mv_data = signal_buffer_data(*record); - v_session.mv_size = signal_buffer_len(*record); - if (mdb_put(transaction, omemo->db->dbi_omemo, - &k_session, &v_session, MDB_NOOVERWRITE)) - { - weechat_printf(NULL, "%sxmpp: failed to read lmdb value", - weechat_prefix("error")); - goto cleanup; - }; + *record = signal_buffer_create(v_session.mv_data, v_session.mv_size); + //*user_record = signal_buffer_create(v_user.mv_data, v_user.mv_size); - if (mdb_txn_commit(transaction)) { - weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", - weechat_prefix("error")); - goto cleanup; - }; - */ - } + weechat_printf(NULL, "-ss_load_session_func\n"); + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", + weechat_prefix("error")); + goto cleanup; + }; - return 0; + return 1; cleanup: + weechat_printf(NULL, "-ss_load_session_func\n"); mdb_txn_abort(transaction); return -1; } @@ -1197,7 +1276,7 @@ cleanup: int ss_get_sub_device_sessions_func(signal_int_list **sessions, const char *name, size_t name_len, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_device_ids = { .mv_data = NULL, .mv_size = strlen("device_ids_") + name_len, @@ -1206,12 +1285,14 @@ int ss_get_sub_device_sessions_func(signal_int_list **sessions, const char *name k_device_ids.mv_data = malloc(sizeof(char) * ( k_device_ids.mv_size + 1)); - snprintf(k_device_ids.mv_data, k_device_ids.mv_size, + snprintf(k_device_ids.mv_data, k_device_ids.mv_size + 1, "device_ids_%s", name); + weechat_printf(NULL, "ss_get_sub_device_sessions_func\n"); if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); + return -1; } if (!mdb_get(transaction, omemo->db->dbi_omemo, @@ -1222,15 +1303,14 @@ int ss_get_sub_device_sessions_func(signal_int_list **sessions, const char *name signal_int_list *list = signal_int_list_alloc(); if (!list) { - mdb_txn_abort(transaction); - return -1; + goto cleanup; } argv = weechat_string_split(v_device_ids.mv_data, " ", NULL, 0, 0, &argc); if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; for (i = 0; i < argc; i++) @@ -1250,12 +1330,15 @@ int ss_get_sub_device_sessions_func(signal_int_list **sessions, const char *name mdb_txn_abort(transaction); return 0; } +cleanup: + mdb_txn_abort(transaction); + return -1; } int ss_store_session_func(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_session = { .mv_data = NULL, .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + @@ -1267,19 +1350,20 @@ int ss_store_session_func(const signal_protocol_address *address, uint8_t *recor .mv_size = strlen("user_") + 10 + //strlen(address->device_id) + 1 + strlen(address->name), }; - MDB_val v_user = {.mv_data = user_record, .mv_size = user_record_len}; + MDB_val v_user = {.mv_data = user_record, .mv_size = user_record_len}; (void) v_user; k_session.mv_data = malloc(sizeof(char) * ( k_session.mv_size + 1)); k_session.mv_size = - snprintf(k_session.mv_data, k_session.mv_size, + snprintf(k_session.mv_data, k_session.mv_size + 1, "session_%u_%s", address->device_id, address->name); k_user.mv_data = malloc(sizeof(char) * ( k_user.mv_size + 1)); k_user.mv_size = - snprintf(k_user.mv_data, k_user.mv_size, + snprintf(k_user.mv_data, k_user.mv_size + 1, "user_%u_%s", address->device_id, address->name); + weechat_printf(NULL, "ss_store_session_func\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -1292,22 +1376,27 @@ int ss_store_session_func(const signal_protocol_address *address, uint8_t *recor &k_user, &v_user, 0)*/) { weechat_printf(NULL, "%sxmpp: failed to write lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; + weechat_printf(NULL, "-ss_store_session_func\n"); if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; return 0; +cleanup: + weechat_printf(NULL, "-ss_store_session_func\n"); + mdb_txn_abort(transaction); + return -1; } int ss_contains_session_func(const signal_protocol_address *address, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_session = { .mv_data = NULL, .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + @@ -1318,29 +1407,31 @@ int ss_contains_session_func(const signal_protocol_address *address, void *user_ k_session.mv_data = malloc(sizeof(char) * ( k_session.mv_size + 1)); k_session.mv_size = - snprintf(k_session.mv_data, k_session.mv_size, + snprintf(k_session.mv_data, k_session.mv_size + 1, "session_%u_%s", address->device_id, address->name); + weechat_printf(NULL, "ss_contains_session_func\n"); if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - return -1; + return 0; } if (mdb_get(transaction, omemo->db->dbi_omemo, &k_session, &v_session)) { + weechat_printf(NULL, "-ss_contains_session_func\n"); mdb_txn_abort(transaction); - return 1; + return 0; }; + weechat_printf(NULL, "-ss_contains_session_func\n"); mdb_txn_abort(transaction); - - return 0; + return 1; } int ss_delete_session_func(const signal_protocol_address *address, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_session = { .mv_data = NULL, .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + @@ -1351,9 +1442,10 @@ int ss_delete_session_func(const signal_protocol_address *address, void *user_da k_session.mv_data = malloc(sizeof(char) * ( k_session.mv_size + 1)); k_session.mv_size = - snprintf(k_session.mv_data, k_session.mv_size, + snprintf(k_session.mv_data, k_session.mv_size + 1, "session_%u_%s", address->device_id, address->name); + weechat_printf(NULL, "ss_delete_session_func\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -1363,16 +1455,19 @@ int ss_delete_session_func(const signal_protocol_address *address, void *user_da if (mdb_del(transaction, omemo->db->dbi_omemo, &k_session, &v_session)) { weechat_printf(NULL, "%sxmpp: failed to erase lmdb value", weechat_prefix("error")); - return -1; + goto cleanup; }; if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; return 1; +cleanup: + mdb_txn_abort(transaction); + return -1; } int ss_delete_all_sessions_func(const char *name, size_t name_len, void *user_data) @@ -1402,7 +1497,7 @@ int sks_store_sender_key(const signal_protocol_sender_key_name *sender_key_name, { struct t_omemo *omemo = (struct t_omemo *)user_data; char *device_list = NULL; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_sender_key = { .mv_data = NULL, .mv_size = strlen("sender_key_") + strlen(sender_key_name->group_id) + @@ -1416,7 +1511,7 @@ int sks_store_sender_key(const signal_protocol_sender_key_name *sender_key_name, 1 + 10 + //strlen(sender_key_name->sender.device_id) + 1 + strlen(sender_key_name->sender.name), }; - MDB_val v_user = {.mv_data = user_record, .mv_size = user_record_len}; + MDB_val v_user = {.mv_data = user_record, .mv_size = user_record_len}; (void) v_user; MDB_val k_device_ids = { .mv_data = NULL, .mv_size = strlen("device_ids_") + strlen(sender_key_name->sender.name), @@ -1426,22 +1521,23 @@ int sks_store_sender_key(const signal_protocol_sender_key_name *sender_key_name, k_sender_key.mv_data = malloc(sizeof(char) * ( k_sender_key.mv_size + 1)); k_sender_key.mv_size = - snprintf(k_sender_key.mv_data, k_sender_key.mv_size, + snprintf(k_sender_key.mv_data, k_sender_key.mv_size + 1, "sender_key_%s_%u_%s", sender_key_name->group_id, sender_key_name->sender.device_id, sender_key_name->sender.name); k_user.mv_data = malloc(sizeof(char) * ( k_user.mv_size + 1)); k_user.mv_size = - snprintf(k_user.mv_data, k_user.mv_size, + snprintf(k_user.mv_data, k_user.mv_size + 1, "user_%s_%u_%s", sender_key_name->group_id, sender_key_name->sender.device_id, sender_key_name->sender.name); k_device_ids.mv_data = malloc(sizeof(char) * ( k_device_ids.mv_size + 1)); - snprintf(k_device_ids.mv_data, k_device_ids.mv_size, + snprintf(k_device_ids.mv_data, k_device_ids.mv_size + 1, "device_ids_%s", sender_key_name->sender.name); + weechat_printf(NULL, "sks_store_sender_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -1462,7 +1558,7 @@ int sks_store_sender_key(const signal_protocol_sender_key_name *sender_key_name, } weechat_string_free_split(argv); - + if (i == argc) { size_t device_list_len = strlen(v_device_ids.mv_data) + 1 + 10 + 1; @@ -1489,25 +1585,27 @@ int sks_store_sender_key(const signal_protocol_sender_key_name *sender_key_name, &k_device_ids, &v_device_ids, 0)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb value", weechat_prefix("error")); - free(device_list); - return -1; + goto cleanup; }; if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); - free(device_list); - return -1; + goto cleanup; }; free(device_list); return 0; +cleanup: + free(device_list); + mdb_txn_abort(transaction); + return -1; } int sks_load_sender_key(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name, void *user_data) { struct t_omemo *omemo = (struct t_omemo *)user_data; - MDB_txn *transaction; + MDB_txn *transaction = NULL; MDB_val k_sender_key = { .mv_data = NULL, .mv_size = strlen("sender_key_") + strlen(sender_key_name->group_id) + @@ -1521,26 +1619,28 @@ int sks_load_sender_key(signal_buffer **record, signal_buffer **user_record, con 1 + 10 + //strlen(sender_key_name->sender.device_id) + 1 + strlen(sender_key_name->sender.name), }; - MDB_val v_user; + MDB_val v_user; (void) v_user; (void) user_record; k_sender_key.mv_data = malloc(sizeof(char) * ( k_sender_key.mv_size + 1)); k_sender_key.mv_size = - snprintf(k_sender_key.mv_data, k_sender_key.mv_size, + snprintf(k_sender_key.mv_data, k_sender_key.mv_size + 1, "sender_key_%s_%u_%s", sender_key_name->group_id, sender_key_name->sender.device_id, sender_key_name->sender.name); k_user.mv_data = malloc(sizeof(char) * ( k_user.mv_size + 1)); k_user.mv_size = - snprintf(k_user.mv_data, k_user.mv_size, + snprintf(k_user.mv_data, k_user.mv_size + 1, "user_%s_%u_%s", sender_key_name->group_id, sender_key_name->sender.device_id, sender_key_name->sender.name); + weechat_printf(NULL, "sks_load_sender_key\n"); if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); + return -1; } if (mdb_get(transaction, omemo->db->dbi_omemo, @@ -1554,53 +1654,401 @@ int sks_load_sender_key(signal_buffer **record, signal_buffer **user_record, con if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; } else { - /* - signal_protocol_key_helper_sender_key_list_node *sender_keys_list; - session_sender_key *sender_key = NULL; + goto cleanup; + } + + return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; +} + +void sks_destroy_func(void *user_data) +{ + struct t_omemo *omemo = (struct t_omemo *)user_data; + (void) omemo; + // Function called to perform cleanup when the data store context is being destroyed +} + +int dls_store_devicelist(const char *jid, signal_int_list *devicelist, struct t_omemo *omemo) +{ + MDB_txn *transaction = NULL; + MDB_val k_devicelist = { + .mv_data = NULL, + .mv_size = strlen("devicelist_") + strlen(jid), + }; + MDB_val v_devicelist; + + k_devicelist.mv_data = malloc(sizeof(char) * ( + k_devicelist.mv_size + 1)); + k_devicelist.mv_size = + snprintf(k_devicelist.mv_data, k_devicelist.mv_size + 1, + "devicelist_%s", jid); + char *devices[128] = {0}; + for (size_t i = 0; i < signal_int_list_size(devicelist); i++) + { + int device = signal_int_list_at(devicelist, i); + devices[i] = malloc(sizeof(*devices) * (10 + 1)); + devices[i+1] = NULL; + snprintf(devices[i], 10 + 1, "%u", device); + } + v_devicelist.mv_data = weechat_string_build_with_split_string( + (const char **)devices, ";"); + v_devicelist.mv_size = strlen(v_devicelist.mv_data); + for (char **device = (char **)devices; *device; device++) free(*device); - signal_protocol_key_helper_generate_sender_keys( - &sender_keys_list, 0, 100, omemo->context); - sender_key = signal_protocol_key_helper_key_list_element(sender_keys_list); - signal_protocol_key_helper_key_list_next(sender_keys_list); + weechat_printf(NULL, "dls_store_devicelist\n"); + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + return -1; + } - uint32_t id = session_sender_key_get_id(sender_key); (void) id; - session_sender_key_serialize(record, sender_key); + if (mdb_put(transaction, omemo->db->dbi_omemo, &k_devicelist, + &v_devicelist, 0)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb value", + weechat_prefix("error")); + goto cleanup; + }; - signal_protocol_key_helper_key_list_free(sender_keys_list); + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + goto cleanup; + }; - v_sender_key.mv_data = signal_buffer_data(*record); - v_sender_key.mv_size = signal_buffer_len(*record); + return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; +} - if (mdb_put(transaction, omemo->db->dbi_omemo, - &k_sender_key, &v_sender_key, MDB_NOOVERWRITE)) - { - weechat_printf(NULL, "%sxmpp: failed to read lmdb value", - weechat_prefix("error")); - return -1; - }; +int dls_load_devicelist(signal_int_list **devicelist, const char *jid, struct t_omemo *omemo) +{ + MDB_txn *transaction = NULL; + MDB_val k_devicelist = { + .mv_data = NULL, + .mv_size = strlen("devicelist_") + strlen(jid), + }; + MDB_val v_devicelist; - if (mdb_txn_commit(transaction)) { - weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + k_devicelist.mv_data = malloc(sizeof(char) * ( + k_devicelist.mv_size + 1)); + k_devicelist.mv_size = + snprintf(k_devicelist.mv_data, k_devicelist.mv_size + 1, + "devicelist_%s", jid); + + weechat_printf(NULL, "dls_load_devicelist\n"); + if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + return -1; + } + + if (mdb_get(transaction, omemo->db->dbi_omemo, + &k_devicelist, &v_devicelist)) + { + goto cleanup; + } + + *devicelist = signal_int_list_alloc(); + int devices_len = 0; + char **devices = weechat_string_split(v_devicelist.mv_data, ";", NULL, 0, 0, &devices_len); + for (int i = 0; i < devices_len; i++) + { + char* device_id = devices[i]; + signal_int_list_push_back(*devicelist, strtol(device_id, NULL, 10)); + } + weechat_string_free_split(devices); + + weechat_printf(NULL, "-dls_load_devicelist\n"); + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", + weechat_prefix("error")); + goto cleanup; + }; + + return 0; +cleanup: + weechat_printf(NULL, "-dls_load_devicelist\n"); + mdb_txn_abort(transaction); + return -1; +} + +int bks_store_bundle(signal_protocol_address *address, + struct t_pre_key **pre_keys, struct t_pre_key **signed_pre_keys, + const char *signature, const char *identity_key, struct t_omemo *omemo) +{ + size_t n_pre_keys = -1; + while (pre_keys[++n_pre_keys] != NULL); + char **pre_key_buffers = malloc(sizeof(char*) * (n_pre_keys + 1)); + for (size_t i = 0; i < n_pre_keys; i++) + { + struct t_pre_key *pre_key = pre_keys[i]; + size_t keylen = 10 + strlen(pre_key->public_key) + 1; + pre_key_buffers[i] = malloc(sizeof(char) * keylen); + pre_key_buffers[i+1] = NULL; + snprintf(pre_key_buffers[i], keylen, + "%s.%s", pre_key->id, pre_key->public_key); + } + + size_t n_signed_pre_keys = -1; + while (signed_pre_keys[++n_signed_pre_keys] != NULL); + char **signed_pre_key_buffers = malloc(sizeof(char*) * (n_signed_pre_keys + 1)); + for (size_t i = 0; i < n_signed_pre_keys; i++) + { + struct t_pre_key *signed_pre_key = signed_pre_keys[i]; + size_t keylen = 10 + 1 + strlen(signed_pre_key->public_key); + signed_pre_key_buffers[i] = malloc(sizeof(char) * (keylen + 1)); + signed_pre_key_buffers[i+1] = NULL; + snprintf(signed_pre_key_buffers[i], keylen + 1, + "%s.%s", signed_pre_key->id, signed_pre_key->public_key); + + int ret; + uint8_t *signing_key_buf; + size_t signing_key_len = base64_decode(identity_key, + strlen(identity_key), &signing_key_buf); + ec_public_key *signing_key; + if ((ret = curve_decode_point(&signing_key, signing_key_buf, + signing_key_len, omemo->context))) { + weechat_printf(NULL, "%sxmpp: failed to decode ED25519 prekey", weechat_prefix("error")); - return -1; + goto cleanup; }; - */ + uint8_t *signed_key_buf; + size_t signed_key_len = base64_decode(signed_pre_key->public_key, + strlen(signed_pre_key->public_key), &signed_key_buf); + uint8_t *signature_buf; + size_t signature_len = base64_decode(signature, + strlen(signature), &signature_buf); + int valid = curve_verify_signature(signing_key, + signed_key_buf, signed_key_len, + signature_buf, signature_len); + if (valid <= 0) { + weechat_printf(NULL, "%somemo: failed to validate ED25519 signature for %s:%u", + weechat_prefix("error"), address->name, address->device_id); + //asm("int3"); + //asm("int3"); + } else + weechat_printf(NULL, "%somemo: succesfully validated ED25519 signature for %s:%u", + weechat_prefix("network"), address->name, address->device_id); + } + + MDB_txn *transaction = NULL; + const char *jid = address->name; + uint32_t device_id = address->device_id; + size_t keylen = strlen("bundle_??_") + strlen(jid) + 1 + 10 + 1; + MDB_val k_bundle_pk = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_pk.mv_size = snprintf(k_bundle_pk.mv_data, keylen, + "bundle_pk_%s_%u", jid, device_id); + MDB_val k_bundle_sk = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_sk.mv_size = snprintf(k_bundle_sk.mv_data, keylen, + "bundle_sk_%s_%u", jid, device_id); + MDB_val k_bundle_sg = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_sg.mv_size = snprintf(k_bundle_sg.mv_data, keylen, + "bundle_sg_%s_%u", jid, device_id); + MDB_val k_bundle_ik = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_ik.mv_size = snprintf(k_bundle_ik.mv_data, keylen, + "bundle_ik_%s_%u", jid, device_id); + + MDB_val v_bundle_pk = { + .mv_data = weechat_string_build_with_split_string( + (const char **)pre_key_buffers, ";"), + .mv_size = 0, + }; + v_bundle_pk.mv_size = strlen(v_bundle_pk.mv_data) + 1; + MDB_val v_bundle_sk = { + .mv_data = weechat_string_build_with_split_string( + (const char **)signed_pre_key_buffers, ";"), + .mv_size = 0, + }; + v_bundle_sk.mv_size = strlen(v_bundle_sk.mv_data) + 1; + MDB_val v_bundle_sg = { + .mv_data = (char*)signature, + .mv_size = strlen(signature), + }; + MDB_val v_bundle_ik = { + .mv_data = (char*)identity_key, + .mv_size = strlen(identity_key), + }; + + weechat_printf(NULL, "bks_store_bundle %s\n", k_bundle_ik.mv_data + 10); + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); return -1; } + int ret; + if ((ret = mdb_put(transaction, omemo->db->dbi_omemo, &k_bundle_pk, + &v_bundle_pk, 0)) || + (ret = mdb_put(transaction, omemo->db->dbi_omemo, &k_bundle_sk, + &v_bundle_sk, 0)) || + (ret = mdb_put(transaction, omemo->db->dbi_omemo, &k_bundle_sg, + &v_bundle_sg, 0)) || + (ret = mdb_put(transaction, omemo->db->dbi_omemo, &k_bundle_ik, + &v_bundle_ik, 0))) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb value '%s'@%u: %s", + weechat_prefix("error"), v_bundle_pk.mv_data, v_bundle_pk.mv_size, mdb_strerror(ret)); + goto cleanup; + }; + + weechat_printf(NULL, "-bks_store_bundle\n"); + if ((ret = mdb_txn_commit(transaction))) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + goto cleanup; + }; + return 0; +cleanup: + weechat_printf(NULL, "-bks_store_bundle (%s!)\n", mdb_strerror(ret)); + mdb_txn_abort(transaction); + return -1; } -void sks_destroy_func(void *user_data) +int bks_load_bundle(session_pre_key_bundle **bundle, signal_protocol_address *address, struct t_omemo *omemo) { - struct t_omemo *omemo = (struct t_omemo *)user_data; - (void) omemo; - // Function called to perform cleanup when the data store context is being destroyed + MDB_txn *transaction = NULL; + const char *jid = address->name; + uint32_t device_id = address->device_id; + size_t keylen = strlen("bundle_??_") + address->name_len + 1 + 10 + 1; + MDB_val k_bundle_pk = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_pk.mv_size = snprintf(k_bundle_pk.mv_data, keylen, + "bundle_pk_%s_%u", jid, device_id); + MDB_val k_bundle_sk = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_sk.mv_size = snprintf(k_bundle_sk.mv_data, keylen, + "bundle_sk_%s_%u", jid, device_id); + MDB_val k_bundle_sg = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_sg.mv_size = snprintf(k_bundle_sg.mv_data, keylen, + "bundle_sg_%s_%u", jid, device_id); + MDB_val k_bundle_ik = { + .mv_data = malloc(sizeof(char) * (keylen + 1)), + .mv_size = 0, + }; + k_bundle_ik.mv_size = snprintf(k_bundle_ik.mv_data, keylen, + "bundle_ik_%s_%u", jid, device_id); + + MDB_val v_bundle_pk; + MDB_val v_bundle_sk; + MDB_val v_bundle_sg; + MDB_val v_bundle_ik; + + int ret; + weechat_printf(NULL, "bks_load_bundle %s\n", k_bundle_ik.mv_data + 10); + if ((ret = mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction))) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + return -1; + } + + if ((ret = mdb_get(transaction, omemo->db->dbi_omemo, + &k_bundle_pk, &v_bundle_pk)) || + (ret = mdb_get(transaction, omemo->db->dbi_omemo, + &k_bundle_sk, &v_bundle_sk)) || + (ret = mdb_get(transaction, omemo->db->dbi_omemo, + &k_bundle_sg, &v_bundle_sg)) || + (ret = mdb_get(transaction, omemo->db->dbi_omemo, + &k_bundle_ik, &v_bundle_ik))) + { + goto cleanup; + } + + int bundle_pk_len = 0; + char **bundle_pks = weechat_string_split(v_bundle_pk.mv_data, ";", NULL, 0, 0, &bundle_pk_len); + uint32_t pre_key_id = 0; + ec_public_key *pre_key; + //for (int i = 0; i < bundle_pk_len; i++) + { + int i = rand() % bundle_pk_len; + char *bundle_pk = bundle_pks[i]; + pre_key_id = strtol(bundle_pk, NULL, 10); + char *key_data = memchr(bundle_pk, '.', 10 + 1) + 1; + uint8_t *key_buf; + size_t key_len = base64_decode(key_data, strlen(key_data), &key_buf); + if ((ret = curve_decode_point(&pre_key, key_buf, key_len, omemo->context))) { + weechat_printf(NULL, "%sxmpp: failed to decode ED25519 prekey", + weechat_prefix("error")); + goto cleanup; + }; + } + int bundle_sk_len; + char **bundle_sks = weechat_string_split(v_bundle_sk.mv_data, ";", NULL, 0, 0, &bundle_sk_len); + uint32_t signed_pre_key_id; + ec_public_key *signed_pre_key; + //for (int i = 0; i < bundle_sk_len; i++) + { + int i = rand() % bundle_sk_len; + char *bundle_sk = bundle_sks[i]; + signed_pre_key_id = strtol(bundle_sk, NULL, 10); + char *key_data = memchr(bundle_sk, '.', 10 + 1) + 1; + uint8_t *key_buf; + size_t key_len = base64_decode(key_data, strlen(key_data), &key_buf); + if ((ret = curve_decode_point(&signed_pre_key, key_buf, key_len, omemo->context))) { + weechat_printf(NULL, "%sxmpp: failed to decode ED25519 signed prekey", + weechat_prefix("error")); + goto cleanup; + }; + } + uint8_t *sig_buf; + size_t sig_len = base64_decode(v_bundle_sg.mv_data, v_bundle_sg.mv_size, &sig_buf); + signal_buffer *signature = signal_buffer_create(sig_buf, sig_len); + ec_public_key *identity_key; + uint8_t *key_buf; + size_t key_len = base64_decode(v_bundle_ik.mv_data, v_bundle_ik.mv_size, &key_buf); + if ((ret = curve_decode_point(&identity_key, key_buf, key_len, omemo->context))) { + weechat_printf(NULL, "%sxmpp: failed to decode ED25519 identity key", + weechat_prefix("error")); + goto cleanup; + }; + + if ((ret = session_pre_key_bundle_create(bundle, device_id, device_id/*?*/, pre_key_id, pre_key, signed_pre_key_id, signed_pre_key, signal_buffer_data(signature), signal_buffer_len(signature), identity_key))) { + weechat_printf(NULL, "%sxmpp: failed to create OMEMO prekey bundle", + weechat_prefix("error")); + goto cleanup; + }; + + weechat_printf(NULL, "-bks_load_bundle\n"); + if ((ret = mdb_txn_commit(transaction))) { + weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", + weechat_prefix("error")); + goto cleanup; + }; + + return 0; +cleanup: + /* + void session_pre_key_bundle_destroy(signal_type_base *type); + */ + weechat_printf(NULL, "-bks_load_bundle: %s\n", mdb_strerror(ret)); + mdb_txn_abort(transaction); + return -1; } void omemo__log_emit_weechat(int level, const char *message, size_t len, void *user_data) @@ -1611,8 +2059,9 @@ void omemo__log_emit_weechat(int level, const char *message, size_t len, void *u const char *tags = level < SG_LOG_DEBUG ? "no_log" : NULL; + (void)buffer; weechat_printf_date_tags( - buffer, 0, tags, + NULL, 0, tags, _("%somemo (%s): %.*s"), weechat_prefix("network"), log_level_name[level], len, message); @@ -1681,9 +2130,9 @@ xmpp_stanza_t *omemo__get_bundle(xmpp_ctx_t *context, char *from, char *to, const uint8_t *keysig = session_signed_pre_key_get_signature(signed_pre_key); size_t keysig_len = session_signed_pre_key_get_signature_len(signed_pre_key); - session_pre_key_destroy((signal_type_base*)signed_pre_key); char *signed_pre_key_signature = NULL; base64_encode(keysig, keysig_len, &signed_pre_key_signature); + session_pre_key_destroy((signal_type_base*)signed_pre_key); children[1] = stanza__iq_pubsub_publish_item_bundle_signedPreKeySignature( context, NULL, NULL); stanza__set_text(context, children[1], with_free(signed_pre_key_signature)); @@ -1711,6 +2160,8 @@ xmpp_stanza_t *omemo__get_bundle(xmpp_ctx_t *context, char *from, char *to, children[0] = stanza__iq_pubsub_publish( context, NULL, children, with_free(bundle_node)); + omemo__handle_bundle(omemo, from, omemo->device_id, children[0]); + children[0] = stanza__iq_pubsub( context, NULL, children, with_noop("http://jabber.org/protocol/pubsub")); @@ -1735,19 +2186,22 @@ void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo, signal_context_create(&new_omemo->context, buffer); signal_context_set_log_function(new_omemo->context, &omemo__log_emit_weechat); + int ret; mdb_env_create(&new_omemo->db->env); mdb_env_set_maxdbs(new_omemo->db->env, 50); - mdb_env_set_mapsize(new_omemo->db->env, (size_t)1048576 * 100000); // 1MB * 100000 - new_omemo->db_path = weechat_string_eval_expression("${weechat_data_dir}/xmpp.omemo.db", - NULL, NULL, NULL); - if (mdb_env_open(new_omemo->db->env, new_omemo->db_path, MDB_NOSUBDIR, 0664) != 0) + mdb_env_set_mapsize(new_omemo->db->env, (size_t)1048576 * 8000); // 8000MB map for valgrind + new_omemo->db_path = weechat_string_eval_expression( + "${weechat_data_dir}/xmpp.omemo.db", NULL, NULL, NULL); + if ((ret = mdb_env_open(new_omemo->db->env, new_omemo->db_path, MDB_NOSUBDIR, 0664)) != 0) { + weechat_printf(NULL, "%sxmpp: failed to open environment file. %s", + weechat_prefix("error"), mdb_strerror(ret)); return; } MDB_txn *parentTransaction = NULL; - MDB_txn *transaction; - if (mdb_txn_begin(new_omemo->db->env, parentTransaction, 0 ? MDB_RDONLY : 0, &transaction)) { + MDB_txn *transaction = NULL; + if (mdb_txn_begin(new_omemo->db->env, parentTransaction, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); return; @@ -1756,7 +2210,7 @@ void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo, size_t db_name_len = strlen("omemo_") + strlen(account_name); char *db_name = malloc(sizeof(char) * (db_name_len + 1)); snprintf(db_name, db_name_len+1, "omemo_%s", account_name); - if (mdb_dbi_open(transaction, db_name, MDB_DUPSORT | MDB_CREATE, &new_omemo->db->dbi_omemo)) { + if (mdb_dbi_open(transaction, db_name, MDB_CREATE, &new_omemo->db->dbi_omemo)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb database", weechat_prefix("error")); return; @@ -1860,6 +2314,8 @@ void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo, signal_buffer_len(private_data), new_omemo->context); ratchet_identity_key_pair_create(&new_omemo->identity, public_key, private_key); } + weechat_printf(buffer, "%somemo: device = %d", + weechat_prefix("info"), new_omemo->device_id); *omemo = new_omemo; } @@ -1873,6 +2329,7 @@ void omemo__handle_devicelist(struct t_omemo *omemo, const char *jid, if (!item) return; xmpp_stanza_t *list = xmpp_stanza_get_child_by_name(item, "list"); if (!list) return; + signal_int_list *devicelist = signal_int_list_alloc(); for (xmpp_stanza_t *device = xmpp_stanza_get_children(list); device; device = xmpp_stanza_get_next(device)) { @@ -1884,16 +2341,19 @@ void omemo__handle_devicelist(struct t_omemo *omemo, const char *jid, if (!device_id) continue; - //weechat_printf(NULL, "omemo devicelist %s: %s%u", - // jid, weechat_color("yellow"), strtol(device_id, NULL, 10)); + signal_int_list_push_back(devicelist, strtol(device_id, NULL, 10)); + weechat_printf(NULL, "omemo devicelist %s: %s%u", + jid, weechat_color("yellow"), strtol(device_id, NULL, 10)); } + if (dls_store_devicelist(jid, devicelist, omemo)) + weechat_printf(NULL, "%somemo: failed to handle devicelist (%s)", + weechat_prefix("error"), jid); + signal_int_list_free(devicelist); } void omemo__handle_bundle(struct t_omemo *omemo, const char *jid, uint32_t device_id, xmpp_stanza_t *items) { - (void) omemo; - xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); if (!item) return; xmpp_stanza_t *bundle = xmpp_stanza_get_child_by_name(item, "bundle"); @@ -1915,6 +2375,13 @@ void omemo__handle_bundle(struct t_omemo *omemo, const char *jid, xmpp_stanza_t *prekeys = xmpp_stanza_get_child_by_name(bundle, "prekeys"); if (!prekeys) return; + int num_prekeys = 0; + for (xmpp_stanza_t *prekey = xmpp_stanza_get_children(prekeys); + prekey; prekey = xmpp_stanza_get_next(prekey)) + num_prekeys++; + struct t_pre_key **pre_keys = malloc(sizeof(struct t_pre_key) * num_prekeys); + + num_prekeys = -1; char **format = weechat_string_dyn_alloc(256); weechat_string_dyn_concat(format, "omemo bundle %s/%u:\n%s..SPK %u: %s\n%3$s..SKS: %s\n%3$s..IK: %s", -1); for (xmpp_stanza_t *prekey = xmpp_stanza_get_children(prekeys); @@ -1931,22 +2398,50 @@ void omemo__handle_bundle(struct t_omemo *omemo, const char *jid, if (!pre_key) continue; + pre_keys[++num_prekeys] = malloc(sizeof(struct t_pre_key)); + *pre_keys[num_prekeys] = (struct t_pre_key) { + .id = pre_key_id, + .public_key = pre_key, + }; + weechat_string_dyn_concat(format, "\n%3$s..PK ", -1); weechat_string_dyn_concat(format, pre_key_id, -1); weechat_string_dyn_concat(format, ": ", -1); weechat_string_dyn_concat(format, pre_key, -1); } + pre_keys[num_prekeys] = NULL; //weechat_printf(NULL, *format, jid, device_id, weechat_color("green"), // signed_pre_key_id, signed_pre_key, // key_signature, identity_key); weechat_string_dyn_free(format, 1); + + struct t_pre_key signed_key = { + .id = signed_pre_key_id, + .public_key = signed_pre_key, + }; + struct t_pre_key *signed_pre_keys[2] = { &signed_key, NULL }; + + signal_protocol_address address = { + .name = jid, .name_len = strlen(jid), .device_id = device_id }; + { + ec_public_key *key; + uint8_t *key_buf; + size_t key_len = base64_decode(identity_key, + strlen(identity_key), &key_buf); + curve_decode_point(&key, key_buf, key_len, omemo->context); + signal_protocol_identity_save_identity(omemo->store_context, + &address, key); + } + bks_store_bundle(&address, pre_keys, signed_pre_keys, + key_signature, identity_key, omemo); } -char *omemo__decode(struct t_omemo *omemo, const char *jid, - xmpp_stanza_t *encrypted) +char *omemo__decode(struct t_account *account, const char *jid, + xmpp_stanza_t *encrypted) { - uint8_t *key_data = NULL, *iv_data = NULL, *payload_data = NULL; - size_t key_len = 0, iv_len = 0, payload_len = 0; + struct t_omemo *omemo = account->omemo; + uint8_t *key_data = NULL, *tag_data = NULL, *iv_data = NULL, *payload_data = NULL; + size_t key_len = 0, tag_len = 0, iv_len = 0, payload_len = 0; xmpp_stanza_t *header = xmpp_stanza_get_child_by_name(encrypted, "header"); if (!header) return NULL; @@ -1955,6 +2450,7 @@ char *omemo__decode(struct t_omemo *omemo, const char *jid, const char *iv__text = xmpp_stanza_get_text(iv); if (!iv__text) return NULL; iv_len = base64_decode(iv__text, strlen(iv__text), &iv_data); + if (iv_len != AES_IV_SIZE) return NULL; char **format = weechat_string_dyn_alloc(256); weechat_string_dyn_concat(format, "omemo msg %s:\n%s..IV: %s", -1); @@ -1969,51 +2465,125 @@ char *omemo__decode(struct t_omemo *omemo, const char *jid, const char *key_id = xmpp_stanza_get_attribute(key, "rid"); if (!key_id) continue; - uint32_t id = strtol(key_id, NULL, 10); - if (id != omemo->device_id) + if (strtol(key_id, NULL, 10) != omemo->device_id) continue; - const char *data = xmpp_stanza_get_text(key); + xmpp_stanza_t *key_text = xmpp_stanza_get_children(key); + const char *data = key_text ? xmpp_stanza_get_text(key_text) : NULL; if (!data) continue; key_len = base64_decode(data, strlen(data), &key_data); - // signal decrypt key for id - weechat_string_dyn_concat(format, "\n%2$s..K ", -1); if (key_prekey) weechat_string_dyn_concat(format, "*", -1); weechat_string_dyn_concat(format, key_id, -1); weechat_string_dyn_concat(format, ": ", -1); weechat_string_dyn_concat(format, data, -1); + + const char *source_id = xmpp_stanza_get_attribute(header, "sid"); + if (!source_id) + continue; + + int ret; + signal_protocol_address address = { + .name = jid, .name_len = strlen(jid), .device_id = strtol(source_id, NULL, 10) }; + signal_message *key_message = NULL; + signal_buffer *aes_key = NULL; + if (key_prekey) { + pre_key_signal_message *pre_key_message = NULL; + if ((ret = pre_key_signal_message_deserialize(&pre_key_message, + key_data, key_len, omemo->context))) return NULL; + ec_public_key *identity_key = pre_key_signal_message_get_identity_key(pre_key_message); + //uint32_t device_id = pre_key_signal_message_get_registration_id(pre_key_message); + //uint32_t pre_key_id = pre_key_signal_message_get_pre_key_id(pre_key_message); + //uint32_t signed_key_id = pre_key_signal_message_get_signed_pre_key_id(pre_key_message); + //ec_public_key *base_key = pre_key_signal_message_get_base_key(pre_key_message); + key_message = pre_key_signal_message_get_signal_message(pre_key_message); + signal_buffer *identity_buf; + if ((ret = ec_public_key_serialize(&identity_buf, identity_key))) return NULL; + if ((ret = iks_save_identity(&address, signal_buffer_data(identity_buf), + signal_buffer_len(identity_buf), omemo))) return NULL; + + session_cipher *cipher; + if ((ret = session_cipher_create(&cipher, omemo->store_context, + &address, omemo->context))) return NULL; + if ((ret = session_cipher_decrypt_pre_key_signal_message(cipher, + pre_key_message, + 0, &aes_key))) return NULL; + } else { + if ((ret = signal_message_deserialize(&key_message, + key_data, key_len, omemo->context))) return NULL; + session_cipher *cipher; + if ((ret = session_cipher_create(&cipher, omemo->store_context, + &address, omemo->context))) return NULL; + if ((ret = session_cipher_decrypt_signal_message(cipher, key_message, + 0, &aes_key))) return NULL; + } + + if (!aes_key) return NULL; + key_data = signal_buffer_data(aes_key); + key_len = signal_buffer_len(aes_key); + if (key_len >= AES_KEY_SIZE) { + tag_len = key_len - AES_KEY_SIZE; + tag_data = key_data + AES_KEY_SIZE; + key_len = AES_KEY_SIZE; + } + else + { + return NULL; + } + + char *aes_key64 = NULL; + if (base64_encode(key_data, key_len, &aes_key64) && aes_key64) + { + weechat_string_dyn_concat(format, "\n%2$s..AES: ", -1); + weechat_string_dyn_concat(format, aes_key64, -1); + weechat_string_dyn_concat(format, " (", -1); + snprintf(aes_key64, strlen(aes_key64), "%lu", key_len); + weechat_string_dyn_concat(format, aes_key64, -1); + weechat_string_dyn_concat(format, ")", -1); + } + if (tag_len && base64_encode(tag_data, tag_len, &aes_key64) && aes_key64) + { + weechat_string_dyn_concat(format, "\n%2$s..TAG: ", -1); + weechat_string_dyn_concat(format, aes_key64, -1); + weechat_string_dyn_concat(format, " (", -1); + snprintf(aes_key64, strlen(aes_key64), "%lu", tag_len); + weechat_string_dyn_concat(format, aes_key64, -1); + weechat_string_dyn_concat(format, ")", -1); + } } xmpp_stanza_t *payload = xmpp_stanza_get_child_by_name(encrypted, "payload"); - if (payload) + if (payload && (payload = xmpp_stanza_get_children(payload))) { - const char *payload__text = xmpp_stanza_get_text(payload); - if (!payload__text) return NULL; - payload_len = base64_decode(payload__text, strlen(payload__text), &payload_data); + const char *payload_text = xmpp_stanza_get_text(payload); + if (!payload_text) return NULL; + payload_len = base64_decode(payload_text, strlen(payload_text), &payload_data); weechat_string_dyn_concat(format, "\n%2$s..PL: ", -1); - weechat_string_dyn_concat(format, payload__text, -1); + weechat_string_dyn_concat(format, payload_text, -1); } weechat_printf(NULL, *format, jid, weechat_color("red"), iv__text); weechat_string_dyn_free(format, 1); - if (!(payload_data && iv_data && key_data) || key_len <= 16) return NULL; + if (!(payload_data && iv_data && key_data)) return NULL; + if (iv_len != AES_IV_SIZE || key_len != AES_KEY_SIZE) return NULL; char *plaintext = NULL; size_t plaintext_len = 0; - if (aes_decrypt(payload_data, payload_len, key_data, iv_data, - key_data+AES_KEY_SIZE, key_len-AES_KEY_SIZE, - (uint8_t**)&plaintext, &plaintext_len) && plaintext) + if (aes_decrypt(payload_data, payload_len, key_data, iv_data, tag_data, tag_len, + (uint8_t**)&plaintext, &plaintext_len) || plaintext) { plaintext[plaintext_len] = '\0'; + weechat_printf(NULL, "%sdecrypted: %.*s", weechat_color("red"), plaintext_len, plaintext); return plaintext; } + weechat_printf(NULL, "%sdecrypt fail", weechat_color("red")); return NULL; } -xmpp_stanza_t *omemo__encode(struct t_omemo *omemo, const char *jid, - uint32_t device_id, const char *unencrypted) +xmpp_stanza_t *omemo__encode(struct t_account *account, const char *jid, + const char *unencrypted) { + struct t_omemo *omemo = account->omemo; uint8_t *key = NULL; uint8_t *iv = NULL; uint8_t *tag = NULL; size_t tag_len = 0; uint8_t *ciphertext = NULL; size_t ciphertext_len = 0; @@ -2035,46 +2605,97 @@ xmpp_stanza_t *omemo__encode(struct t_omemo *omemo, const char *jid, base64_encode(ciphertext, ciphertext_len, &ciphertext64); free(ciphertext); - signal_buffer *record; - signal_protocol_address address = { - .name = jid, .name_len = strlen(jid), .device_id = device_id}; + xmpp_stanza_t *encrypted = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(encrypted, "encrypted"); + xmpp_stanza_set_ns(encrypted, "eu.siacs.conversations.axolotl"); + xmpp_stanza_t *header = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(header, "header"); + char device_id_str[10+1] = {0}; + snprintf(device_id_str, 10+1, "%u", omemo->device_id); + xmpp_stanza_set_attribute(header, "sid", device_id_str); + + int ret, keycount = 0; + signal_int_list *devicelist; + if ((ret = dls_load_devicelist(&devicelist, jid, omemo))) return NULL; + for (size_t i = 0; i < signal_int_list_size(devicelist); i++) + { + uint32_t device_id = signal_int_list_at(devicelist, i); + signal_protocol_address address = { + .name = jid, .name_len = strlen(jid), .device_id = device_id}; + + xmpp_stanza_t *header__key = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(header__key, "key"); + char device_id_str[10+1] = {0}; + snprintf(device_id_str, 10+1, "%u", device_id); + xmpp_stanza_set_attribute(header__key, "rid", device_id_str); + + session_builder *builder = NULL; + if (((ret = ss_contains_session_func(&address, omemo))) <= 0) + { + session_pre_key_bundle *bundle; + weechat_printf(NULL, "%somemo send deviceid %u", weechat_color("blue"), device_id); + if ((ret = bks_load_bundle(&bundle, &address, omemo))) continue; - /* - session_builder *builder; - session_builder_create(&builder, omemo->store_context, &address, omemo->context); - //session_builder_process_pre_key_bundle(builder, pre_key); - - session_cipher *cipher; - session_cipher_create(&cipher, omemo->store_context, &address, omemo->context); - - ciphertext_message *encrypted_message; - session_cipher_encrypt(cipher, key_and_tag, AES_KEY_SIZE+tag_len, &encrypted_message); - signal_buffer *serialized = ciphertext_message_get_serialized(encrypted_message); - int prekey = ciphertext_message_get_type(encrypted_message) == CIPHERTEXT_PREKEY_TYPE - ? 1 : 0; - - char *payload = NULL; - base64_encode(signal_buffer_data(serialized), signal_buffer_len(serialized), - &payload); - weechat_printf(NULL, "signal send %s/%u: %s%s\n..%sMessage: %s", - jid, device_id, weechat_color("blue"), key64, - prekey ? "PreKey" : "", payload); - - signal_buffer_free(serialized); - SIGNAL_UNREF(encrypted_message); - session_cipher_free(cipher); - session_builder_free(builder); - */ + if ((ret = session_builder_create(&builder, omemo->store_context, &address, omemo->context))) continue; + if ((ret = session_builder_process_pre_key_bundle(builder, bundle))) continue; + weechat_printf(NULL, "%somemo bundle ok", weechat_color("blue"), device_id); + } + + session_cipher *cipher; + if ((ret = session_cipher_create(&cipher, omemo->store_context, &address, omemo->context))) continue; + weechat_printf(NULL, "%somemo cipher ok", weechat_color("blue")); + + ciphertext_message *signal_message; + if ((ret = session_cipher_encrypt(cipher, key_and_tag, AES_KEY_SIZE+tag_len, &signal_message))) continue; + weechat_printf(NULL, "%somemo encrypt ok", weechat_color("blue")); + signal_buffer *record = ciphertext_message_get_serialized(signal_message); + int prekey = ciphertext_message_get_type(signal_message) == CIPHERTEXT_PREKEY_TYPE + ? 1 : 0; + weechat_printf(NULL, "%somemo prekey %s", weechat_color("blue"), prekey ? "true" : "false"); + + char *payload = NULL; + base64_encode(signal_buffer_data(record), signal_buffer_len(record), + &payload); + + if (prekey) + xmpp_stanza_set_attribute(header__key, "prekey", + prekey ? "true" : "false"); + stanza__set_text(account->context, header__key, with_free(payload)); + xmpp_stanza_add_child(header, header__key); + xmpp_stanza_release(header__key); + + keycount++; + + signal_buffer_free(record); + //SIGNAL_UNREF(signal_message); + session_cipher_free(cipher); + if (builder) + session_builder_free(builder); + } + signal_int_list_free(devicelist); free(key_and_tag); - // ... - weechat_printf(NULL, "omemo send %s/%u: %s%s\n..Key: %s\n..IV: %s\n..Payload: %s", - jid, device_id, weechat_color("blue"), unencrypted, - key64, iv64, ciphertext64); + if (keycount == 0) return NULL; + + xmpp_stanza_t *header__iv = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(header__iv, "iv"); + stanza__set_text(account->context, header__iv, with_noop(iv64)); + xmpp_stanza_add_child(header, header__iv); + xmpp_stanza_release(header__iv); + xmpp_stanza_add_child(encrypted, header); + xmpp_stanza_release(header); + xmpp_stanza_t *encrypted__payload = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(encrypted__payload, "payload"); + stanza__set_text(account->context, encrypted__payload, with_noop(ciphertext64)); + xmpp_stanza_add_child(encrypted, encrypted__payload); + xmpp_stanza_release(encrypted__payload); + + weechat_printf(NULL, "%somemo send %s: %s", + weechat_color("blue"), jid, stanza_xml(encrypted)); free(iv64); free(key64); free(ciphertext64); - return NULL; + return encrypted; } void omemo__free(struct t_omemo *omemo) diff --git a/omemo.h b/omemo.h index bf28992..80c9ebe 100644 --- a/omemo.h +++ b/omemo.h @@ -46,11 +46,11 @@ void omemo__handle_devicelist(struct t_omemo *omemo, const char *jid, void omemo__handle_bundle(struct t_omemo *omemo, const char *jid, uint32_t device_id, xmpp_stanza_t *items); -char *omemo__decode(struct t_omemo *omemo, const char *jid, +char *omemo__decode(struct t_account *account, const char *jid, xmpp_stanza_t *encrypted); -xmpp_stanza_t *omemo__encode(struct t_omemo *omemo, const char *jid, - uint32_t device_id, const char *unencrypted); +xmpp_stanza_t *omemo__encode(struct t_account *account, const char *jid, + const char *unencrypted); void omemo__free(struct t_omemo *omemo); diff --git a/xmpp/iq.c b/xmpp/iq.c index 53e437a..f5d14a6 100644 --- a/xmpp/iq.c +++ b/xmpp/iq.c @@ -98,9 +98,36 @@ xmpp_stanza_t *stanza__iq_pubsub_items(xmpp_ctx_t *context, xmpp_stanza_t *base, if (node) { xmpp_stanza_set_attribute(parent, "node", node); + } + + return parent; +} + +xmpp_stanza_t *stanza__iq_pubsub_subscribe(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *node, struct t_string *jid) +{ + xmpp_stanza_t *parent = base; + + if (!parent) + { + parent = xmpp_stanza_new(context); + xmpp_stanza_set_name(parent, "subscribe"); + } + + if (node) + { + xmpp_stanza_set_attribute(parent, "node", node->value); + node->finalize(node); free(node); } + if (jid) + { + xmpp_stanza_set_attribute(parent, "jid", jid->value); + jid->finalize(jid); + free(jid); + } + return parent; } @@ -408,3 +435,31 @@ xmpp_stanza_t *stanza__iq_ping(xmpp_ctx_t *context, xmpp_stanza_t *base, return parent; } + +xmpp_stanza_t *stanza__iq_query(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *ns, struct t_string *node) +{ + xmpp_stanza_t *parent = base; + + if (!parent) + { + parent = xmpp_stanza_new(context); + xmpp_stanza_set_name(parent, "query"); + } + + if (ns) + { + xmpp_stanza_set_ns(parent, ns->value); + ns->finalize(ns); + free(ns); + } + + if (node) + { + xmpp_stanza_set_attribute(parent, "node", node->value); + node->finalize(node); + free(node); + } + + return parent; +} diff --git a/xmpp/stanza.h b/xmpp/stanza.h index 9858541..f893ab5 100644 --- a/xmpp/stanza.h +++ b/xmpp/stanza.h @@ -78,6 +78,9 @@ xmpp_stanza_t *stanza__iq_pubsub(xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t *stanza__iq_pubsub_items(xmpp_ctx_t *context, xmpp_stanza_t *base, char *node); +xmpp_stanza_t *stanza__iq_pubsub_subscribe(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *node, struct t_string *jid); + xmpp_stanza_t *stanza__iq_pubsub_publish(xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, struct t_string *node); @@ -114,4 +117,7 @@ xmpp_stanza_t *stanza__iq_enable(xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t *stanza__iq_ping(xmpp_ctx_t *context, xmpp_stanza_t *base, struct t_string *ns); +xmpp_stanza_t *stanza__iq_query(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *ns, struct t_string *node); + #endif /*WEECHAT_XMPP_STANZA_H*/