From b97a9bd9efe8ecd3b647bc577159fec74630a823 Mon Sep 17 00:00:00 2001 From: Tony Olagbaiye Date: Fri, 31 Dec 2021 14:49:52 +0000 Subject: [PATCH] interstitial commit --- .envrc | 3 +- Makefile | 4 +- account.c | 60 ++- account.h | 6 +- buffer.c | 1 + channel.c | 33 +- channel.h | 5 + command.c | 2 +- completion.c | 1 + config.c | 1 + connection.c | 295 +++++----- input.c | 1 + libomemo.scm | 126 +++++ message.c | 1 + omemo.c | 1435 +++++++++++++++++++++++++++++++++++++++++-------- omemo.h | 32 +- plugin.c | 1 + user.c | 2 + user.h | 1 + xmpp/iq.c | 9 +- xmpp/stanza.h | 18 +- 21 files changed, 1653 insertions(+), 384 deletions(-) create mode 100644 libomemo.scm diff --git a/.envrc b/.envrc index aeb2bc0..7d42a8c 100644 --- a/.envrc +++ b/.envrc @@ -49,9 +49,10 @@ use_guix() libxml2 # Dep (libxml2) libstrophe # Dep (strophe) libgcrypt # Dep (gcrypt) - libsignal-protocol-c # Dep (libsignal) + -l libomemo.scm #libsignal-protocol-c # Dep (libsignal) lmdb # Dep (lmdb) rnp # Dep (rnpgp) + glib sqlite minixml ) # Thanks diff --git a/Makefile b/Makefile index 1e89f09..15b077f 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ ifdef DEBUG endif RM=rm -f FIND=find -INCLUDES=-Ilibstrophe $(shell xml2-config --cflags) $(shell pkg-config --cflags librnp-0) $(shell pkg-config --cflags libsignal-protocol-c) +INCLUDES=-Ilibstrophe $(shell xml2-config --cflags) $(shell pkg-config --cflags librnp-0) $(shell pkg-config --cflags libomemo-c) CFLAGS+=$(DBGCFLAGS) -fno-omit-frame-pointer -fPIC -std=gnu99 -gdwarf-4 -Wall -Wextra -Werror-implicit-function-declaration -Wno-missing-field-initializers -D_XOPEN_SOURCE=700 $(INCLUDES) LDFLAGS+=$(DBGLDFLAGS) -shared -g $(DBGCFLAGS) -LDLIBS=-lstrophe -lpthread $(shell xml2-config --libs) $(shell pkg-config --libs librnp-0) $(shell pkg-config --libs libsignal-protocol-c) -lgcrypt -llmdb +LDLIBS=-lstrophe -lpthread $(shell xml2-config --libs) $(shell pkg-config --libs librnp-0) $(shell pkg-config --libs libomemo-c) -lgcrypt -llmdb PREFIX ?= /usr/local LIBDIR ?= $(PREFIX)/lib diff --git a/account.c b/account.c index 638765d..883b553 100644 --- a/account.c +++ b/account.c @@ -12,6 +12,7 @@ #include #include "plugin.h" +#include "xmpp/stanza.h" #include "config.h" #include "input.h" #include "omemo.h" @@ -90,7 +91,7 @@ int account__search_option(const char *option_name) return -1; } -struct t_account_device *account__search_device(struct t_account *account, int id) +struct t_account_device *account__search_device(struct t_account *account, uint32_t id) { struct t_account_device *ptr_device; @@ -118,6 +119,7 @@ void account__add_device(struct t_account *account, new_device = malloc(sizeof(*new_device)); new_device->id = device->id; new_device->name = strdup(device->name); + new_device->label = device->label ? strdup(device->label) : NULL; new_device->prev_device = account->last_device; new_device->next_device = NULL; @@ -151,6 +153,8 @@ void account__free_device(struct t_account *account, struct t_account_device *de (device->next_device)->prev_device = device->prev_device; /* free device data */ + if (device->label) + free(device->label); if (device->name) free(device->name); @@ -165,6 +169,60 @@ void account__free_device_all(struct t_account *account) account__free_device(account, account->devices); } +xmpp_stanza_t *account__get_devicelist(struct t_account *account) +{ + xmpp_stanza_t *parent, **children; + struct t_account_device *device; + const char *ns, *node; + char id[64] = {0}; + int i = 0; + + device = malloc(sizeof(struct t_account_device)); + + device->id = account->omemo->device_id; + snprintf(id, sizeof(id), "%u", device->id); + device->name = strdup(id); + device->label = strdup("weechat"); + + 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")); + + free(device->label); + free(device->name); + free(device); + + for (device = account->devices; device; + device = device->next_device) + { + if (device->id != account->omemo->device_id) + children[i++] = stanza__iq_pubsub_publish_item_list_device( + account->context, NULL, with_noop(device->name), NULL); + } + + children[i] = NULL; + node = "eu.siacs.conversations.axolotl"; + children[0] = stanza__iq_pubsub_publish_item_list( + account->context, NULL, children, with_noop(node)); + children[1] = NULL; + children[0] = stanza__iq_pubsub_publish_item( + account->context, NULL, children, NULL); + node = "eu.siacs.conversations.axolotl.devicelist"; + children[0] = stanza__iq_pubsub_publish(account->context, + NULL, children, + with_noop(node)); + ns = "http://jabber.org/protocol/pubsub"; + children[0] = stanza__iq_pubsub(account->context, NULL, + children, with_noop(ns)); + parent = stanza__iq(account->context, NULL, + children, NULL, strdup("announce1"), + NULL, NULL, strdup("set")); + free(children); + + return parent; +} + struct t_account_mam_query *account__add_mam_query(struct t_account *account, struct t_channel *channel, const char *id, diff --git a/account.h b/account.h index 4234749..321afd4 100644 --- a/account.h +++ b/account.h @@ -66,8 +66,9 @@ enum t_account_option struct t_account_device { - int id; + uint32_t id; char *name; + char *label; struct t_account_device *prev_device; struct t_account_device *next_device; @@ -128,10 +129,11 @@ extern char *account_options[][2]; struct t_account *account__search(const char *account_name); struct t_account *account__casesearch (const char *account_name); int account__search_option(const char *option_name); -struct t_account_device *account__search_device(struct t_account *account, int id); +struct t_account_device *account__search_device(struct t_account *account, uint32_t id); void account__add_device(struct t_account *account, struct t_account_device *device); void account__free_device(struct t_account *account, struct t_account_device *device); void account__free_device_all(struct t_account *account); +xmpp_stanza_t *account__get_devicelist(struct t_account *account); struct t_account_mam_query *account__add_mam_query(struct t_account *account, struct t_channel *channel, const char *id, diff --git a/buffer.c b/buffer.c index 3d5ed3e..6bda7b8 100644 --- a/buffer.c +++ b/buffer.c @@ -2,6 +2,7 @@ // License, version 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include #include #include #include diff --git a/channel.c b/channel.c index 1ad04f6..a3e289b 100644 --- a/channel.c +++ b/channel.c @@ -18,6 +18,7 @@ #include "input.h" #include "buffer.h" #include "pgp.h" +#include "util.h" const char *channel__transport_name(enum t_channel_transport transport) { @@ -276,6 +277,11 @@ struct t_channel *channel__new(struct t_account *account, new_channel->name = strdup(name); new_channel->transport = CHANNEL_TRANSPORT_PLAINTEXT; new_channel->pgp_id = NULL; + new_channel->omemo.session = 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->topic.value = NULL; new_channel->topic.creator = NULL; @@ -916,10 +922,13 @@ struct t_channel_member *channel__add_member(struct t_account *account, user->profile.status_text ? " [" : "", user->profile.status_text ? user->profile.status_text : "", user->profile.status_text ? "]" : "", - user->profile.pgp_id ? weechat_color("gray") : "", - user->profile.pgp_id ? " with PGP:" : "", + user->profile.pgp_id || user->profile.omemo ? weechat_color("gray") : "", + user->profile.pgp_id || user->profile.omemo ? " with " : "", + user->profile.pgp_id ? "PGP:" : "", user->profile.pgp_id ? user->profile.pgp_id : "", - user->profile.pgp_id ? weechat_color("reset") : ""); + user->profile.omemo && user->profile.pgp_id ? " and " : "", + user->profile.omemo ? "OMEMO" : "", + user->profile.pgp_id || user->profile.omemo ? weechat_color("reset") : ""); return member; } @@ -999,8 +1008,21 @@ void channel__send_message(struct t_account *account, struct t_channel *channel, xmpp_stanza_set_id(message, id); xmpp_free(account->context, id); - char *url = strstr(body, "http"); - if (channel->pgp_id) + if (account->omemo && channel->omemo.session >= 0) + { + xmpp_message_set_body(message, body); + + if (channel->transport != CHANNEL_TRANSPORT_OMEMO) + { + 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)); + } + + omemo__encode(account->omemo, to, 0, body); + } + else if (channel->pgp_id) { xmpp_stanza_t *message__x = xmpp_stanza_new(account->context); xmpp_stanza_set_name(message__x, "x"); @@ -1049,6 +1071,7 @@ void channel__send_message(struct t_account *account, struct t_channel *channel, } } + char *url = strstr(body, "http"); if (url) { xmpp_stanza_t *message__x = xmpp_stanza_new(account->context); diff --git a/channel.h b/channel.h index 37863f1..9d45019 100644 --- a/channel.h +++ b/channel.h @@ -64,6 +64,11 @@ struct t_channel char *name; enum t_channel_transport transport; char *pgp_id; + struct { + int session; + struct t_hashtable *devicelist_requests; + struct t_hashtable *bundle_requests; + } omemo; struct t_channel_topic topic; diff --git a/command.c b/command.c index 57c7019..165414c 100644 --- a/command.c +++ b/command.c @@ -4,13 +4,13 @@ #include #include +#include #include #include #include #include #include "plugin.h" -//#include "oauth.h" #include "account.h" #include "user.h" #include "channel.h" diff --git a/completion.c b/completion.c index 9fb976a..721a06c 100644 --- a/completion.c +++ b/completion.c @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include #include diff --git a/config.c b/config.c index fc89b78..df4171d 100644 --- a/config.c +++ b/config.c @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include #include diff --git a/connection.c b/connection.c index aa8c8ed..34ea101 100644 --- a/connection.c +++ b/connection.c @@ -86,7 +86,7 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void struct t_account *account = (struct t_account *)userdata; struct t_user *user; struct t_channel *channel; - xmpp_stanza_t *iq__x_signed, *iq__x_muc_user, *show, *idle, *iq__x__item, *iq__c, *iq__status; + 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 *show__text = NULL, *idle__since = NULL, *certificate = NULL, *node = NULL, *ver = NULL; char *clientid = NULL, *status; @@ -101,18 +101,18 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void show__text = show ? xmpp_stanza_get_text(show) : NULL; idle = xmpp_stanza_get_child_by_name_and_ns(stanza, "idle", "urn:xmpp:idle:1"); idle__since = idle ? xmpp_stanza_get_attribute(idle, "since") : NULL; - iq__x_signed = xmpp_stanza_get_child_by_name_and_ns( + pres__x_signed = xmpp_stanza_get_child_by_name_and_ns( stanza, "x", "jabber:x:signed"); - if (iq__x_signed) + if (pres__x_signed) { - certificate = xmpp_stanza_get_text(iq__x_signed); + certificate = xmpp_stanza_get_text(pres__x_signed); } - iq__c = xmpp_stanza_get_child_by_name_and_ns( + pres__c = xmpp_stanza_get_child_by_name_and_ns( stanza, "c", "http://jabber.org/protocol/caps"); - if (iq__c) + if (pres__c) { - node = xmpp_stanza_get_attribute(iq__c, "node"); - ver = xmpp_stanza_get_attribute(iq__c, "ver"); + node = xmpp_stanza_get_attribute(pres__c, "node"); + ver = xmpp_stanza_get_attribute(pres__c, "ver"); if (node && ver) { int len = strlen(node)+1+strlen(ver); @@ -120,25 +120,25 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void snprintf(clientid, len, "%s#%s", node, ver); } } - iq__status = xmpp_stanza_get_child_by_name(stanza, "status"); - status = iq__status ? xmpp_stanza_get_text(iq__status) : NULL; - iq__x_muc_user = xmpp_stanza_get_child_by_name_and_ns( + pres__status = xmpp_stanza_get_child_by_name(stanza, "status"); + status = pres__status ? xmpp_stanza_get_text(pres__status) : NULL; + pres__x_muc_user = xmpp_stanza_get_child_by_name_and_ns( stanza, "x", "http://jabber.org/protocol/muc#user"); channel = channel__search(account, from_bare); - if (weechat_strcasecmp(type, "unavailable") && !iq__x_muc_user && !channel) + if (weechat_strcasecmp(type, "unavailable") != 0 && !pres__x_muc_user && !channel) channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); - if (iq__x_muc_user) - for (iq__x__item = xmpp_stanza_get_children(iq__x_muc_user); - iq__x__item; iq__x__item = xmpp_stanza_get_next(iq__x__item)) + if (pres__x_muc_user) + 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(iq__x__item), "item") != 0) + if (weechat_strcasecmp(xmpp_stanza_get_name(pres__x__item), "item") != 0) continue; - role = xmpp_stanza_get_attribute(iq__x__item, "role"); - affiliation = xmpp_stanza_get_attribute(iq__x__item, "affiliation"); - jid = xmpp_stanza_get_attribute(iq__x__item, "jid"); + role = xmpp_stanza_get_attribute(pres__x__item, "role"); + affiliation = xmpp_stanza_get_attribute(pres__x__item, "affiliation"); + jid = xmpp_stanza_get_attribute(pres__x__item, "jid"); user = user__search(account, from); if (!user) @@ -209,7 +209,7 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * struct t_account *account = (struct t_account *)userdata; struct t_channel *channel; - xmpp_stanza_t *x, *body, *delay, *topic, *replace, *request, *markable, *composing, *sent, *received, *result, *forwarded; + xmpp_stanza_t *x, *body, *delay, *topic, *replace, *request, *markable, *composing, *sent, *received, *result, *forwarded, *event, *items, *item, *list, *device, *encrypted, **children; const char *type, *from, *nick, *from_bare, *to, *to_bare, *id, *thread, *replace_id, *timestamp; char *text, *intext, *difftext = NULL, *cleartext = NULL; struct tm time = {0}; @@ -222,6 +222,9 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * if (topic != NULL) { intext = xmpp_stanza_get_text(topic); + type = xmpp_stanza_get_type(stanza); + if (type != NULL && strcmp(type, "error") == 0) + return 1; from = xmpp_stanza_get_from(stanza); if (from == NULL) return 1; @@ -229,7 +232,12 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * from = xmpp_jid_resource(account->context, from); channel = channel__search(account, from_bare); if (!channel) - channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); + { + if (weechat_strcasecmp(type, "groupchat") == 0) + channel = channel__new(account, CHANNEL_TYPE_MUC, from_bare, from_bare); + else + channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); + } channel__update_topic(channel, intext ? intext : "", from, 0); if (intext != NULL) xmpp_free(account->context, intext); @@ -297,6 +305,69 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * } } + event = xmpp_stanza_get_child_by_name_and_ns( + stanza, "event", "http://jabber.org/protocol/pubsub#event"); + if (event) + { + items = xmpp_stanza_get_child_by_name(event, "items"); + if (items) + { + const char *items_node = xmpp_stanza_get_attribute(items, "node"); + from = xmpp_stanza_get_from(stanza); + to = xmpp_stanza_get_to(stanza); + if (items_node + && weechat_strcasecmp(items_node, + "eu.siacs.conversations.axolotl.devicelist") == 0) + { + item = xmpp_stanza_get_child_by_name(items, "item"); + if (item) + { + list = xmpp_stanza_get_child_by_name_and_ns( + item, "list", "eu.siacs.conversations.axolotl"); + if (list) + { + if (account->omemo) + { + omemo__handle_devicelist(account->omemo, from, + items); + } + + children = malloc(sizeof(*children) * (3 + 1)); + + 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")); + children[0] = + stanza__iq(account->context, NULL, children, NULL, strdup("fetch1"), + strdup(to), strdup(from), strdup("get")); + + xmpp_send(conn, children[0]); + xmpp_stanza_release(children[0]); + } + } + } + } + } + } + return 1; } type = xmpp_stanza_get_type(stanza); @@ -382,6 +453,12 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * weechat_list_add(channel->unreads, unread->id, WEECHAT_LIST_POS_END, unread); } + encrypted = xmpp_stanza_get_child_by_name_and_ns(stanza, "encrypted", + "eu.siacs.conversations.axolotl"); + if (encrypted && account->omemo) + { + omemo__decode(account->omemo, from_bare, encrypted); + } x = xmpp_stanza_get_child_by_name_and_ns(stanza, "x", "jabber:x:encrypted"); intext = xmpp_stanza_get_text(body); if (x) @@ -557,7 +634,7 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * weechat_string_dyn_concat(dyn_tags, replace_id, -1); } - if (date != 0) + if (date != 0 || encrypted) weechat_string_dyn_concat(dyn_tags, ",notify_none", -1); else if (channel->type == CHANNEL_TYPE_PM && weechat_strcasecmp(from_bare, account_jid(account)) != 0) @@ -609,11 +686,12 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd { struct t_account *account = (struct t_account *)userdata; xmpp_stanza_t *reply, *query, *identity, *feature, *x, *field, *value, *text, *fin; - xmpp_stanza_t *pubsub, *items, *item, *list, *device, **children; + 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); 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"); @@ -836,7 +914,7 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd stanza, "pubsub", "http://jabber.org/protocol/pubsub"); if (pubsub) { - const char *items_node, *item_id, *device_id, *ns, *node; + const char *items_node, *item_id, *device_id; items = xmpp_stanza_get_child_by_name(pubsub, "items"); if (items) @@ -855,122 +933,75 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd item, "list", "eu.siacs.conversations.axolotl"); if (list && account->omemo) { - account__free_device_all(account); + omemo__handle_devicelist(account->omemo, from, items); - struct t_account_device *dev; - char id[64] = {0}; - int i = 0; + if (weechat_strcasecmp(account_jid(account), from) == 0) + { + struct t_account_device *dev; + char id[64] = {0}; - dev = malloc(sizeof(struct t_account_device)); + account__free_device_all(account); - dev->id = account->omemo->device_id; - snprintf(id, sizeof(id), "%d", dev->id); - dev->name = strdup(id); - account__add_device(account, dev); + dev = malloc(sizeof(struct t_account_device)); - children = malloc(sizeof(xmpp_stanza_t *) * 128); - children[i++] = stanza__iq_pubsub_publish_item_list_device( - account->context, NULL, with_noop(dev->name)); + dev->id = account->omemo->device_id; + snprintf(id, sizeof(id), "%d", dev->id); + dev->name = strdup(id); + dev->label = strdup("weechat"); + account__add_device(account, dev); - free(dev->name); - free(dev); + free(dev->label); + free(dev->name); + free(dev); - 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; + 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; - device_id = xmpp_stanza_get_id(device); + device_id = xmpp_stanza_get_id(device); - dev = malloc(sizeof(struct t_account_device)); - dev->id = atoi(device_id); - dev->name = strdup(device_id); - account__add_device(account, dev); + dev = malloc(sizeof(struct t_account_device)); + dev->id = atoi(device_id); + dev->name = strdup(device_id); + dev->label = NULL; + account__add_device(account, dev); - children[i++] = stanza__iq_pubsub_publish_item_list_device( - account->context, NULL, with_noop(dev->name)); + free(dev->label); + free(dev->name); + free(dev); + } - free(dev->name); - free(dev); + reply = account__get_devicelist(account); + xmpp_send(conn, reply); + xmpp_stanza_release(reply); + } + } + } + } + if (items_node + && strncmp(items_node, + "eu.siacs.conversations.axolotl.bundles", + strnlen(items_node, + strlen("eu.siacs.conversations.axolotl.bundles"))) == 0) + { + item = xmpp_stanza_get_child_by_name(items, "item"); + if (item) + { + bundle = xmpp_stanza_get_child_by_name_and_ns(item, "bundle", "eu.siacs.conversations.axolotl"); + if (bundle) + { + size_t node_prefix = + strlen("eu.siacs.conversations.axolotl.bundles:"); + if (account->omemo && strlen(items_node) > node_prefix) + { + omemo__handle_bundle(account->omemo, from, + strtol(items_node+node_prefix, + NULL, 10), + items); } - - children[i] = NULL; - node = "eu.siacs.conversations.axolotl"; - children[0] = stanza__iq_pubsub_publish_item_list( - account->context, NULL, children, with_noop(node)); - children[1] = NULL; - children[0] = stanza__iq_pubsub_publish_item( - account->context, NULL, children, with_noop("current")); - ns = "http://jabber.org/protocol/pubsub"; - children[0] = stanza__iq_pubsub_publish(account->context, - NULL, children, - with_noop(ns)); - children[0] = stanza__iq_pubsub(account->context, NULL, - children, with_noop("")); - reply = stanza__iq(account->context, xmpp_stanza_reply(stanza), - children, NULL, strdup("announce1"), - NULL, NULL, strdup("set")); - - xmpp_send(conn, reply); - xmpp_stanza_release(reply); - - char bundle_node[128] = {0}; - snprintf(bundle_node, sizeof(bundle_node), - "eu.siacs.conversations.axolotl.bundles:%d", - account->omemo->device_id); - - xmpp_stanza_t *textchild[2] = {NULL}; - textchild[0] = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(textchild[0], "b64enc1"); - children[0] = stanza__iq_pubsub_publish_item_bundle_signedPreKeyPublic( - account->context, NULL, textchild, with_noop("1")); - textchild[0] = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(textchild[0], "b64enc2"); - children[1] = stanza__iq_pubsub_publish_item_bundle_signedPreKeySignature( - account->context, NULL, textchild); - textchild[0] = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(textchild[0], "b64enc3"); - children[2] = stanza__iq_pubsub_publish_item_bundle_identityKey( - account->context, NULL, textchild); - textchild[0] = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(textchild[0], "b64enc4"); - children[3] = stanza__iq_pubsub_publish_item_bundle_prekeys_preKeyPublic( - account->context, NULL, textchild, with_noop("1")); - textchild[0] = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(textchild[0], "b64enc5"); - children[4] = stanza__iq_pubsub_publish_item_bundle_prekeys_preKeyPublic( - account->context, NULL, textchild, with_noop("2")); - textchild[0] = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(textchild[0], "b64enc6"); - children[5] = stanza__iq_pubsub_publish_item_bundle_prekeys_preKeyPublic( - account->context, NULL, textchild, with_noop("3")); - children[6] = NULL; - children[3] = stanza__iq_pubsub_publish_item_bundle_prekeys( - account->context, NULL, &children[3]); - children[4] = NULL; - ns = "eu.siacs.conversations.axolotl"; - children[0] = stanza__iq_pubsub_publish_item_bundle( - account->context, NULL, children, with_noop(ns)); - children[1] = NULL; - children[0] = stanza__iq_pubsub_publish_item( - account->context, NULL, children, with_noop("current")); - children[0] = stanza__iq_pubsub_publish(account->context, - NULL, children, - with_noop(bundle_node)); - 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("announce2"), - strdup(account_jid(account)), strdup(account_jid(account)), - strdup("set")); - - xmpp_send(conn, children[0]); - xmpp_stanza_release(children[0]); - - free(children); } } } @@ -1185,6 +1216,12 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, 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]); + (void) weechat_hook_signal_send("xmpp_account_connected", WEECHAT_HOOK_SIGNAL_STRING, account->name); } diff --git a/input.c b/input.c index dc9fd6f..6df232e 100644 --- a/input.c +++ b/input.c @@ -4,6 +4,7 @@ #include #include +#include #include #include "plugin.h" diff --git a/libomemo.scm b/libomemo.scm new file mode 100644 index 0000000..28e141a --- /dev/null +++ b/libomemo.scm @@ -0,0 +1,126 @@ +(define-module (gnu packages messaging) + #:use-module (gnu packages) + #:use-module (gnu packages admin) + #:use-module (gnu packages aidc) + #:use-module (gnu packages aspell) + #:use-module (gnu packages audio) + #:use-module (gnu packages autotools) + #:use-module (gnu packages avahi) + #:use-module (gnu packages base) + #:use-module (gnu packages bash) + #:use-module (gnu packages bison) + #:use-module (gnu packages boost) + #:use-module (gnu packages check) + #:use-module (gnu packages compression) + #:use-module (gnu packages cpp) + #:use-module (gnu packages crypto) + #:use-module (gnu packages curl) + #:use-module (gnu packages cyrus-sasl) + #:use-module (gnu packages databases) + #:use-module (gnu packages docbook) + #:use-module (gnu packages documentation) + #:use-module (gnu packages enchant) + #:use-module (gnu packages fontutils) + #:use-module (gnu packages freedesktop) + #:use-module (gnu packages gettext) + #:use-module (gnu packages glib) + #:use-module (gnu packages gnome) + #:use-module (gnu packages gnupg) + #:use-module (gnu packages golang) + #:use-module (gnu packages gperf) + #:use-module (gnu packages graphviz) + #:use-module (gnu packages gstreamer) + #:use-module (gnu packages gtk) + #:use-module (gnu packages guile) + #:use-module (gnu packages icu4c) + #:use-module (gnu packages image) + #:use-module (gnu packages kde) + #:use-module (gnu packages kerberos) + #:use-module (gnu packages less) + #:use-module (gnu packages libcanberra) + #:use-module (gnu packages libffi) + #:use-module (gnu packages libidn) + #:use-module (gnu packages libreoffice) + #:use-module (gnu packages linux) + #:use-module (gnu packages logging) + #:use-module (gnu packages lua) + #:use-module (gnu packages man) + #:use-module (gnu packages markup) + #:use-module (gnu packages matrix) + #:use-module (gnu packages mono) + #:use-module (gnu packages mpd) + #:use-module (gnu packages ncurses) + #:use-module (gnu packages networking) + #:use-module (gnu packages nss) + #:use-module (gnu packages pcre) + #:use-module (gnu packages perl) + #:use-module (gnu packages photo) + #:use-module (gnu packages php) + #:use-module (gnu packages pkg-config) + #:use-module (gnu packages protobuf) + #:use-module (gnu packages python) + #:use-module (gnu packages python-check) + #:use-module (gnu packages python-crypto) + #:use-module (gnu packages python-web) + #:use-module (gnu packages python-xyz) + #:use-module (gnu packages qt) + #:use-module (gnu packages readline) + #:use-module (gnu packages ruby) + #:use-module (gnu packages sphinx) + #:use-module (gnu packages sqlite) + #:use-module (gnu packages tcl) + #:use-module (gnu packages texinfo) + #:use-module (gnu packages textutils) + #:use-module (gnu packages tls) + #:use-module (gnu packages video) + #:use-module (gnu packages web) + #:use-module (gnu packages xdisorg) + #:use-module (gnu packages xiph) + #:use-module (gnu packages xml) + #:use-module (gnu packages xorg) + #:use-module (guix build-system cmake) + #:use-module (guix build-system go) + #:use-module (guix build-system glib-or-gtk) + #:use-module (guix build-system gnu) + #:use-module (guix build-system meson) + #:use-module (guix build-system perl) + #:use-module (guix build-system python) + #:use-module (guix build-system qt) + #:use-module (guix build-system trivial) + #:use-module (guix download) + #:use-module (guix git-download) + #:use-module (guix hg-download) + #:use-module ((guix licenses) #:prefix license:) + #:use-module (guix packages) + #:use-module (guix utils)) + +(define-public libomemo-c + (package + (name "libomemo-c") + (version "2.3.3") + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/dino/libomemo-c") + (commit "06184660790daa42433e616fa3dee730717d1c1b"))) + (file-name (git-file-name name version)) + (sha256 + (base32 + "1wa85r1b54myabsbmbqlq9jz174mmvd02b8zy1j4j0kml0anp6nx")))) + (arguments + `(;; Required for proper linking and for tests to run. + #:configure-flags '("-DBUILD_SHARED_LIBS=on" "-DBUILD_TESTING=1"))) + (build-system cmake-build-system) + (inputs `( ;; Required for tests: + ("check" ,check) + ("openssl" ,openssl))) + (native-inputs `(("pkg-config" ,pkg-config))) + (home-page "https://github.com/WhisperSystems/libsignal-protocol-c") + (synopsis "Implementation of a ratcheting forward secrecy protocol") + (description "libsignal-protocol-c is an implementation of a ratcheting +forward secrecy protocol that works in synchronous and asynchronous +messaging environments. It can be used with messaging software to provide +end-to-end encryption.") + (license license:gpl3+))) + +libomemo-c diff --git a/message.c b/message.c index 0314d44..d896527 100644 --- a/message.c +++ b/message.c @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/omemo.c b/omemo.c index 811a508..1c30d63 100644 --- a/omemo.c +++ b/omemo.c @@ -6,9 +6,13 @@ #include #include #include +#include #include #include #include +#include +#include +#include #include #include #include @@ -20,8 +24,10 @@ struct t_omemo_db { }; #include "plugin.h" +#include "xmpp/stanza.h" #include "account.h" #include "omemo.h" +#include "util.h" #define mdb_val_str(s) { \ .mv_data = s, .mv_size = strlen(s), \ @@ -35,8 +41,71 @@ struct t_omemo_db { .mv_data = NULL, .mv_size = sizeof(t), \ } +#define PRE_KEY_START 1 +#define PRE_KEY_COUNT 100 + +#define AES_KEY_SIZE (16) +#define AES_IV_SIZE (12) + 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); + 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)); + return weechat_string_base_encode(64, (char*)buffer, length, *result); +} + +int aes_decrypt(const uint8_t *ciphertext, size_t ciphertext_len, + uint8_t *key, uint8_t *iv, uint8_t *tag, size_t tag_len, + uint8_t **plaintext, size_t *plaintext_len) +{ + gcry_cipher_hd_t cipher = NULL; + if (gcry_cipher_open(&cipher, GCRY_CIPHER_AES192, + 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 = malloc((sizeof(uint8_t) * *plaintext_len) + 1); + if (gcry_cipher_decrypt(cipher, *plaintext, *plaintext_len, + ciphertext, ciphertext_len)) goto cleanup; + if (gcry_cipher_checktag(cipher, tag, tag_len)) goto cleanup; + return 1; +cleanup: + gcry_cipher_close(cipher); + return 0; +} + +int aes_encrypt(const uint8_t *plaintext, size_t plaintext_len, + uint8_t **key, uint8_t **iv, uint8_t **tag, size_t *tag_len, + uint8_t **ciphertext, size_t *ciphertext_len) +{ + *tag_len = 16; + *tag = gcry_random_bytes(*tag_len, GCRY_STRONG_RANDOM); + *iv = gcry_random_bytes(AES_IV_SIZE, GCRY_STRONG_RANDOM); + *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, + 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 = malloc((sizeof(uint8_t) * *ciphertext_len) + 1); + if (gcry_cipher_encrypt(cipher, *ciphertext, *ciphertext_len, + plaintext, plaintext_len)) goto cleanup; + if (gcry_cipher_gettag(cipher, *tag, *tag_len)) goto cleanup; + return 1; +cleanup: + gcry_cipher_close(cipher); + return 0; +} + void signal_protocol_address_free(signal_protocol_address* ptr) { if (!ptr) return; @@ -413,16 +482,16 @@ int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **priva MDB_val k_local_public_key = mdb_val_str("local_public_key"); MDB_val v_local_private_key, v_local_public_key; - // Get the local client's identity key pair - if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); + goto cleanup; } - if (mdb_get(transaction, omemo->db->dbi_omemo, - &k_local_private_key, &v_local_private_key) && - mdb_get(transaction, omemo->db->dbi_omemo, - &k_local_public_key, &v_local_public_key)) + if (!mdb_get(transaction, omemo->db->dbi_omemo, + &k_local_private_key, &v_local_private_key) && + !mdb_get(transaction, omemo->db->dbi_omemo, + &k_local_public_key, &v_local_public_key)) { *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); @@ -430,21 +499,13 @@ int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **priva if (mdb_txn_commit(transaction)) { weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; } else { struct ratchet_identity_key_pair *identity; - mdb_txn_abort(transaction); - - if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { - weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", - weechat_prefix("error")); - return -1; - } - signal_protocol_key_helper_generate_identity_key_pair( &identity, omemo->context); ec_private_key *private_key = ratchet_identity_key_pair_get_private(identity); @@ -465,17 +526,25 @@ int iks_get_identity_key_pair(signal_buffer **public_data, signal_buffer **priva { 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; }; + + *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); } return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; } int iks_get_local_registration_id(void *user_data, uint32_t *registration_id) @@ -486,36 +555,30 @@ int iks_get_local_registration_id(void *user_data, uint32_t *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, MDB_RDONLY, &transaction)) { + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; } - if (mdb_get(transaction, omemo->db->dbi_omemo, - &k_local_registration_id, - &v_local_registration_id)) + if (!mdb_get(transaction, omemo->db->dbi_omemo, + &k_local_registration_id, + &v_local_registration_id)) { *registration_id = *(uint32_t*)v_local_registration_id.mv_data; if (mdb_txn_commit(transaction)) { - weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_printf(NULL, "%sxmpp: failed to read lmdb transaction", weechat_prefix("error")); - return -1; + goto cleanup; }; } else { - mdb_txn_abort(transaction); - - 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 generated_id; signal_protocol_key_helper_generate_registration_id( - (uint32_t*)&v_local_registration_id.mv_data, 0, omemo->context); + &generated_id, 0, omemo->context); + v_local_registration_id.mv_data = &generated_id; if (mdb_put(transaction, omemo->db->dbi_omemo, &k_local_registration_id, @@ -523,17 +586,22 @@ int iks_get_local_registration_id(void *user_data, uint32_t *registration_id) { 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; }; + + *registration_id = generated_id; } return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; } int iks_save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data) @@ -560,7 +628,6 @@ int iks_save_identity(const signal_protocol_address *address, uint8_t *key_data, snprintf(k_identity_key.mv_data, k_identity_key.mv_size, "identity_key_%s", address->name); - // Save a remote client's identity key if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -610,7 +677,6 @@ int iks_is_trusted_identity(const signal_protocol_address *address, uint8_t *key snprintf(k_identity_key.mv_data, k_identity_key.mv_size, "identity_key_%s", address->name); - // Verify a remote client's identity key if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); @@ -621,7 +687,7 @@ int iks_is_trusted_identity(const signal_protocol_address *address, uint8_t *key &v_registration_id) || mdb_get(transaction, omemo->db->dbi_omemo, &k_identity_key, &v_identity_key)) { - weechat_printf(NULL, "%sxmpp: failed to write lmdb value", + weechat_printf(NULL, "%sxmpp: failed to read lmdb value", weechat_prefix("error")); return -1; }; @@ -648,289 +714,893 @@ void iks_destroy_func(void *user_data) // Function called to perform cleanup when the data store context is being destroyed } -int pks_load_pre_key(signal_buffer **record, uint32_t pre_key_id, 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_val k_pre_key = { + .mv_data = NULL, + .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) + }; + MDB_val v_pre_key = {.mv_data = record, .mv_size = record_len}; + + 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, + "pre_key_%-10u", pre_key_id); + + 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_pre_key, + &v_pre_key, 0)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb value", + weechat_prefix("error")); + return -1; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + return -1; + }; + + return 0; +} + +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_val k_pre_key = mdb_val_str("pre_key"); + MDB_val k_pre_key = { + .mv_data = NULL, + .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) + }; MDB_val v_pre_key; + 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, + "pre_key_%-10u", pre_key_id); + 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; } - if (mdb_get(transaction, omemo->db->dbi_omemo, - &k_pre_key, &v_pre_key)) + if (mdb_get(transaction, omemo->db->dbi_omemo, &k_pre_key, + &v_pre_key)) { + weechat_printf(NULL, "%sxmpp: failed to read lmdb value", + weechat_prefix("error")); + mdb_txn_abort(transaction); + return 0; + }; + + mdb_txn_abort(transaction); + + return 1; +} + +uint32_t pks_get_count(struct t_omemo *omemo, int increment) +{ + uint32_t count = PRE_KEY_START; + MDB_txn *transaction; + 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); + + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + goto cleanup; + } + + if (!mdb_get(transaction, omemo->db->dbi_omemo, + &k_pre_key_idx, &v_pre_key_idx)) + { + if (increment) + count += PRE_KEY_COUNT; + } + + if (mdb_put(transaction, omemo->db->dbi_omemo, + &k_pre_key_idx, &v_pre_key_idx, 0)) + { + weechat_printf(NULL, "%sxmpp: failed to read lmdb value", + weechat_prefix("error")); + goto cleanup; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + goto cleanup; + }; + + return count; +cleanup: + mdb_txn_abort(transaction); + return 0; +} + +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_val k_pre_key = { + .mv_data = NULL, + .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) + }; + MDB_val v_pre_key; + + 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, + "pre_key_%-10u", pre_key_id); + + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + goto cleanup; + } + + if (!mdb_get(transaction, omemo->db->dbi_omemo, + &k_pre_key, &v_pre_key)) { *record = signal_buffer_create(v_pre_key.mv_data, v_pre_key.mv_size); 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_pre_key_list_node *pre_keys_list; - session_pre_key *pre_key = NULL; - mdb_txn_abort(transaction); - /* - signal_protocol_key_helper_generate_pre_keys( - &pre_keys_list, 0, 100, omemo->context); - pre_key = signal_protocol_key_helper_key_list_element(pre_keys_list); - signal_protocol_key_helper_key_list_next(pre_keys_list); - - uint32_t id = session_pre_key_get_id(pre_key); - session_pre_key_serialize(&record, pre_key); + signal_protocol_key_helper_pre_key_list_node *pre_keys_list; + session_pre_key *pre_key = NULL; + for (signal_protocol_key_helper_generate_pre_keys(&pre_keys_list, + pks_get_count(omemo, 1), PRE_KEY_COUNT, + omemo->context); pre_keys_list; + pre_keys_list = signal_protocol_key_helper_key_list_next(pre_keys_list)) + { + pre_key = signal_protocol_key_helper_key_list_element(pre_keys_list); + uint32_t id = session_pre_key_get_id(pre_key); + session_pre_key_serialize(record, pre_key); + pks_store_pre_key(id, signal_buffer_data(*record), + signal_buffer_len(*record), user_data); + } signal_protocol_key_helper_key_list_free(pre_keys_list); + } - if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { - weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", - weechat_prefix("error")); - return -1; - } + return 0; +cleanup: + mdb_txn_abort(transaction); + return -1; +} - v_pre_key.mv_data = signal_buffer_data(*record); - v_pre_key.mv_size = signal_buffer_len(*record); +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_val k_pre_key = { + .mv_data = NULL, + .mv_size = strlen("pre_key_") + 10, // strlen(UINT32_MAX) + }; + MDB_val v_pre_key; - if (mdb_put(transaction, omemo->db->dbi_omemo, - &k_pre_key, &v_pre_key, MDB_NOOVERWRITE)) - { - weechat_printf(NULL, "%sxmpp: failed to write lmdb value", - weechat_prefix("error")); - return -1; - }; + 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, + "pre_key_%-10u", pre_key_id); - if (mdb_txn_commit(transaction)) { - weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", - weechat_prefix("error")); - return -1; - }; - */ + 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_del(transaction, omemo->db->dbi_omemo, &k_pre_key, + &v_pre_key)) { + weechat_printf(NULL, "%sxmpp: failed to erase lmdb value", + weechat_prefix("error")); + return -1; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", + weechat_prefix("error")); + return -1; + }; + return 0; } -int pks_store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data) +void pks_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 spks_load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data) { - (void) pre_key_id; - (void) record; - (void) record_len; - (void) user_data; - return -1; struct t_omemo *omemo = (struct t_omemo *)user_data; MDB_txn *transaction; - MDB_val k_pre_key = mdb_val_str("pre_key"); - MDB_val v_pre_key; + MDB_val k_signed_pre_key = { + .mv_data = NULL, + .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) + }; + MDB_val v_signed_pre_key; - if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { + 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, + "signed_pre_key_%-10u", signed_pre_key_id); + + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); + goto cleanup; } - if (mdb_get(transaction, omemo->db->dbi_omemo, - &k_pre_key, &v_pre_key)) + if (!mdb_get(transaction, omemo->db->dbi_omemo, + &k_signed_pre_key, &v_signed_pre_key)) { - *record = signal_buffer_create(v_pre_key.mv_data, v_pre_key.mv_size); + *record = signal_buffer_create(v_signed_pre_key.mv_data, v_signed_pre_key.mv_size); 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_pre_key_list_node *pre_keys_list; - session_pre_key *pre_key = NULL; - - mdb_txn_abort(transaction); - - /* - signal_protocol_key_helper_generate_pre_keys( - &pre_keys_list, 0, 100, omemo->context); - pre_key = signal_protocol_key_helper_key_list_element(pre_keys_list); - signal_protocol_key_helper_key_list_next(pre_keys_list); - - uint32_t id = session_pre_key_get_id(pre_key); - session_pre_key_serialize(&record, pre_key); + session_signed_pre_key *signed_pre_key = NULL; + signal_buffer *serialized_key = NULL; - signal_protocol_key_helper_key_list_free(pre_keys_list); - - if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { - weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", - weechat_prefix("error")); - return -1; - } + signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, omemo->identity, signed_pre_key_id, time(NULL), omemo->context); + session_signed_pre_key_serialize(&serialized_key, signed_pre_key); - v_pre_key.mv_data = signal_buffer_data(*record); - v_pre_key.mv_size = signal_buffer_len(*record); + v_signed_pre_key.mv_data = signal_buffer_data(serialized_key); + v_signed_pre_key.mv_size = signal_buffer_len(serialized_key); if (mdb_put(transaction, omemo->db->dbi_omemo, - &k_pre_key, &v_pre_key, MDB_NOOVERWRITE)) + &k_signed_pre_key, &v_signed_pre_key, MDB_NOOVERWRITE)) { - weechat_printf(NULL, "%sxmpp: failed to write lmdb value", + weechat_printf(NULL, "%sxmpp: failed to read 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 -1; + + *record = serialized_key; } return 0; -} - -int pks_contains_pre_key(uint32_t pre_key_id, void *user_data) -{ - (void) pre_key_id; - (void) user_data; - return -1; -} - -int pks_remove_pre_key(uint32_t pre_key_id, void *user_data) -{ - (void) pre_key_id; - (void) user_data; - return -1; -} - -void pks_destroy_func(void *user_data) -{ - (void) user_data; -} - -int spks_load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data) -{ - (void) record; - (void) signed_pre_key_id; - (void) user_data; +cleanup: + mdb_txn_abort(transaction); return -1; - //session_signed_pre_key *signed_pre_key; - //int start_id = 0; - //time_t timestamp = time(NULL); - //signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, new_omemo->identity, 5, timestamp, new_omemo->context); } int spks_store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data) { - (void) signed_pre_key_id; - (void) record; - (void) record_len; - (void) user_data; - return -1; -} + struct t_omemo *omemo = (struct t_omemo *)user_data; + MDB_txn *transaction; + MDB_val k_signed_pre_key = { + .mv_data = NULL, + .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) + }; + MDB_val v_signed_pre_key = {.mv_data = record, .mv_size = record_len}; -int spks_contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) -{ - (void) signed_pre_key_id; - (void) user_data; - return -1; -} + 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, + "signed_pre_key_%-10u", signed_pre_key_id); -int spks_remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) -{ - (void) signed_pre_key_id; - (void) user_data; - return -1; + 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_signed_pre_key, + &v_signed_pre_key, 0)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb value", + weechat_prefix("error")); + return -1; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + return -1; + }; + + return 0; +} + +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_val k_signed_pre_key = { + .mv_data = NULL, + .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) + }; + MDB_val v_signed_pre_key; + + 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, + "signed_pre_key_%-10u", signed_pre_key_id); + + 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; + } + + if (mdb_get(transaction, omemo->db->dbi_omemo, &k_signed_pre_key, + &v_signed_pre_key)) { + mdb_txn_abort(transaction); + return 0; + }; + + mdb_txn_abort(transaction); + + return 1; +} + +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_val k_signed_pre_key = { + .mv_data = NULL, + .mv_size = strlen("signed_pre_key_") + 10, // strlen(UINT32_MAX) + }; + MDB_val v_signed_pre_key; + + 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, + "signed_pre_key_%-10u", signed_pre_key_id); + + 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_del(transaction, omemo->db->dbi_omemo, &k_signed_pre_key, + &v_signed_pre_key)) { + weechat_printf(NULL, "%sxmpp: failed to erase lmdb value", + weechat_prefix("error")); + return -1; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", + weechat_prefix("error")); + return -1; + }; + + return 0; } void spks_destroy_func(void *user_data) { - (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 ss_load_session_func(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address, void *user_data) { - (void) record; - (void) user_record; - (void) address; - (void) user_data; + struct t_omemo *omemo = (struct t_omemo *)user_data; + MDB_txn *transaction; + MDB_val k_session = { + .mv_data = NULL, + .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + + 1 + strlen(address->name), + }; + MDB_val v_session; + MDB_val k_user = { + .mv_data = NULL, + .mv_size = strlen("user_") + 10 + //strlen(address->device_id) + + 1 + strlen(address->name), + }; + MDB_val 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, + "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, + "user_%u_%s", address->device_id, address->name); + + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + goto cleanup; + } + + 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; + }; + } + 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; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + goto cleanup; + }; + */ + } + + return 0; +cleanup: + mdb_txn_abort(transaction); return -1; } int ss_get_sub_device_sessions_func(signal_int_list **sessions, const char *name, size_t name_len, void *user_data) { - (void) sessions; - (void) name; - (void) name_len; - (void) user_data; - return -1; + struct t_omemo *omemo = (struct t_omemo *)user_data; + MDB_txn *transaction; + MDB_val k_device_ids = { + .mv_data = NULL, + .mv_size = strlen("device_ids_") + name_len, + }; + MDB_val v_device_ids; + + 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, + "device_ids_%s", name); + + if (mdb_txn_begin(omemo->db->env, NULL, MDB_RDONLY, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + } + + if (!mdb_get(transaction, omemo->db->dbi_omemo, + &k_device_ids, &v_device_ids)) + { + char **argv; + int argc, i; + signal_int_list *list = signal_int_list_alloc(); + + if (!list) { + mdb_txn_abort(transaction); + return -1; + } + + 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; + }; + + for (i = 0; i < argc; i++) + { + char* device_id = argv[i]; + + signal_int_list_push_back(list, strtol(device_id, NULL, 10)); + } + + weechat_string_free_split(argv); + + *sessions = list; + return argc; + } + else + { + mdb_txn_abort(transaction); + return 0; + } } 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) { - (void) address; - (void) record; - (void) record_len; - (void) user_record; - (void) user_record_len; - (void) user_data; - return -1; + struct t_omemo *omemo = (struct t_omemo *)user_data; + MDB_txn *transaction; + MDB_val k_session = { + .mv_data = NULL, + .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + + 1 + strlen(address->name), + }; + MDB_val v_session = {.mv_data = record, .mv_size = record_len}; + MDB_val k_user = { + .mv_data = NULL, + .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}; + + 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, + "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, + "user_%u_%s", address->device_id, address->name); + + 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_session, &v_session, 0)/* || + mdb_put(transaction, omemo->db->dbi_omemo, + &k_user, &v_user, 0)*/) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb value", + weechat_prefix("error")); + return -1; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + return -1; + }; + + return 0; } int ss_contains_session_func(const signal_protocol_address *address, void *user_data) { - (void) address; - (void) user_data; - return -1; + struct t_omemo *omemo = (struct t_omemo *)user_data; + MDB_txn *transaction; + MDB_val k_session = { + .mv_data = NULL, + .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + + 1 + strlen(address->name), + }; + MDB_val v_session; + + 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, + "session_%u_%s", address->device_id, address->name); + + 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_session, &v_session)) { + mdb_txn_abort(transaction); + return 1; + }; + + mdb_txn_abort(transaction); + + return 0; } int ss_delete_session_func(const signal_protocol_address *address, void *user_data) { - (void) address; - (void) user_data; - return -1; + struct t_omemo *omemo = (struct t_omemo *)user_data; + MDB_txn *transaction; + MDB_val k_session = { + .mv_data = NULL, + .mv_size = strlen("session_") + 10 + //strlen(address->device_id) + + 1 + strlen(address->name), + }; + MDB_val v_session; + + 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, + "session_%u_%s", address->device_id, address->name); + + 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_del(transaction, omemo->db->dbi_omemo, &k_session, &v_session)) { + weechat_printf(NULL, "%sxmpp: failed to erase lmdb value", + weechat_prefix("error")); + return -1; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", + weechat_prefix("error")); + return -1; + }; + + return 1; } int ss_delete_all_sessions_func(const char *name, size_t name_len, void *user_data) { - (void) name; - (void) name_len; - (void) user_data; + signal_int_list *sessions; + ss_get_sub_device_sessions_func(&sessions, name, name_len, user_data); + + int n = signal_int_list_size(sessions); + for (int i = 0; i < n; i++) + { + signal_protocol_address address = {.name = name, .name_len = name_len, + .device_id = signal_int_list_at(sessions, i)}; + ss_delete_session_func(&address, user_data); + } + signal_int_list_free(sessions); return -1; } void ss_destroy_func(void *user_data) { - (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 sks_store_sender_key(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data) { - (void) sender_key_name; - (void) record; - (void) record_len; - (void) user_record; - (void) user_record_len; - (void) user_data; - return -1; + struct t_omemo *omemo = (struct t_omemo *)user_data; + char *device_list = NULL; + MDB_txn *transaction; + MDB_val k_sender_key = { + .mv_data = NULL, + .mv_size = strlen("sender_key_") + strlen(sender_key_name->group_id) + + 1 + 10 + //strlen(sender_key_name->sender.device_id) + + 1 + strlen(sender_key_name->sender.name), + }; + MDB_val v_sender_key = {.mv_data = record, .mv_size = record_len}; + MDB_val k_user = { + .mv_data = NULL, + .mv_size = strlen("user_") + strlen(sender_key_name->group_id) + + 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 k_device_ids = { + .mv_data = NULL, + .mv_size = strlen("device_ids_") + strlen(sender_key_name->sender.name), + }; + MDB_val v_device_ids; + + 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, + "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, + "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, + "device_ids_%s", sender_key_name->sender.name); + + 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, + &k_device_ids, &v_device_ids)) + { + char **argv; + int argc, i; + + argv = weechat_string_split(v_device_ids.mv_data, " ", NULL, 0, 0, &argc); + for (i = 0; i < argc; i++) + { + char* device_id = argv[i]; + if (strtol(device_id, NULL, 10) == sender_key_name->sender.device_id) break; + } + + weechat_string_free_split(argv); + + if (i == argc) + { + size_t device_list_len = strlen(v_device_ids.mv_data) + 1 + 10 + 1; + device_list = malloc(sizeof(char) * device_list_len); + snprintf(device_list, device_list_len, "%s %u", + (char*)v_device_ids.mv_data, sender_key_name->sender.device_id); + v_device_ids.mv_data = device_list; + v_device_ids.mv_size = strlen(device_list) + 1; + } + } + else + { + device_list = malloc(sizeof(char) * (10 + 1)); + snprintf(device_list, 10 + 1, "%u", sender_key_name->sender.device_id); + v_device_ids.mv_data = device_list; + v_device_ids.mv_size = strlen(device_list) + 1; + } + + if (mdb_put(transaction, omemo->db->dbi_omemo, + &k_sender_key, &v_sender_key, 0)/* || + mdb_put(transaction, omemo->db->dbi_omemo, + &k_user, &v_user, 0)*/ || + mdb_put(transaction, omemo->db->dbi_omemo, + &k_device_ids, &v_device_ids, 0)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb value", + weechat_prefix("error")); + free(device_list); + return -1; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + free(device_list); + return -1; + }; + free(device_list); + + return 0; } int sks_load_sender_key(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name, void *user_data) { - (void) record; - (void) user_record; - (void) sender_key_name; - (void) user_data; - return -1; + struct t_omemo *omemo = (struct t_omemo *)user_data; + MDB_txn *transaction; + MDB_val k_sender_key = { + .mv_data = NULL, + .mv_size = strlen("sender_key_") + strlen(sender_key_name->group_id) + + 1 + 10 + //strlen(sender_key_name->sender.device_id) + + 1 + strlen(sender_key_name->sender.name), + }; + MDB_val v_sender_key; + MDB_val k_user = { + .mv_data = NULL, + .mv_size = strlen("user_") + strlen(sender_key_name->group_id) + + 1 + 10 + //strlen(sender_key_name->sender.device_id) + + 1 + strlen(sender_key_name->sender.name), + }; + MDB_val v_user; + + 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, + "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, + "user_%s_%u_%s", sender_key_name->group_id, + sender_key_name->sender.device_id, + sender_key_name->sender.name); + + if (mdb_txn_begin(omemo->db->env, NULL, 0, &transaction)) { + weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", + weechat_prefix("error")); + } + + if (mdb_get(transaction, omemo->db->dbi_omemo, + &k_sender_key, &v_sender_key)/* && + mdb_get(transaction, omemo->db->dbi_omemo, + &k_user, &v_user)*/) + { + *record = signal_buffer_create(v_sender_key.mv_data, v_sender_key.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")); + return -1; + }; + } + else + { + /* + signal_protocol_key_helper_sender_key_list_node *sender_keys_list; + session_sender_key *sender_key = NULL; + + 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); + + uint32_t id = session_sender_key_get_id(sender_key); (void) id; + session_sender_key_serialize(record, sender_key); + + signal_protocol_key_helper_key_list_free(sender_keys_list); + + v_sender_key.mv_data = signal_buffer_data(*record); + v_sender_key.mv_size = signal_buffer_len(*record); + + 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; + }; + + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", + weechat_prefix("error")); + return -1; + }; + */ + return -1; + } + + return 0; } void sks_destroy_func(void *user_data) { - (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 } void omemo__log_emit_weechat(int level, const char *message, size_t len, void *user_data) @@ -948,6 +1618,109 @@ void omemo__log_emit_weechat(int level, const char *message, size_t len, void *u log_level_name[level], len, message); } +xmpp_stanza_t *omemo__get_bundle(xmpp_ctx_t *context, char *from, char *to, + struct t_omemo *omemo) +{ + xmpp_stanza_t **children = malloc(sizeof(*children) * (100 + 1)); + xmpp_stanza_t *parent = NULL; + signal_buffer *record = NULL; + ec_key_pair *keypair = NULL; + ec_public_key *public_key = NULL; + + int num_keys = 0; + for (uint32_t id = PRE_KEY_START; + id < INT_MAX && num_keys < 100; id++) + { + if (pks_load_pre_key(&record, id, omemo) != 0) continue; + else num_keys++; + session_pre_key *pre_key = NULL; + session_pre_key_deserialize(&pre_key, signal_buffer_data(record), + signal_buffer_len(record), omemo->context); + if (pre_key == 0) (*((int*)0))++; + signal_buffer_free(record); + keypair = session_pre_key_get_key_pair(pre_key); + public_key = ec_key_pair_get_public(keypair); + ec_public_key_serialize(&record, public_key); + char *data = NULL; + base64_encode(signal_buffer_data(record), + signal_buffer_len(record), &data); + signal_buffer_free(record); + if (pre_key) session_pre_key_destroy((signal_type_base*)pre_key); + //SIGNAL_UNREF(pre_key); + char *id_str = malloc(sizeof(char) * (10 + 1)); + snprintf(id_str, 10+1, "%u", id); + children[num_keys-1] = stanza__iq_pubsub_publish_item_bundle_prekeys_preKeyPublic( + context, NULL, NULL, with_free(id_str)); + stanza__set_text(context, children[num_keys-1], with_free(data)); + } + children[100] = NULL; + + children[3] = stanza__iq_pubsub_publish_item_bundle_prekeys( + context, NULL, children); + children[4] = NULL; + + spks_load_signed_pre_key(&record, 1, omemo); + session_signed_pre_key *signed_pre_key; + session_signed_pre_key_deserialize(&signed_pre_key, + signal_buffer_data(record), signal_buffer_len(record), + omemo->context); + signal_buffer_free(record); + uint32_t signed_pre_key_id = session_signed_pre_key_get_id(signed_pre_key); + keypair = session_signed_pre_key_get_key_pair(signed_pre_key); + public_key = ec_key_pair_get_public(keypair); + ec_public_key_serialize(&record, public_key); + char *signed_pre_key_public = NULL; + base64_encode(signal_buffer_data(record), signal_buffer_len(record), + &signed_pre_key_public); + signal_buffer_free(record); + char *signed_pre_key_id_str = malloc(sizeof(char) * (10 + 1)); + snprintf(signed_pre_key_id_str, 10+1, "%u", signed_pre_key_id); + children[0] = stanza__iq_pubsub_publish_item_bundle_signedPreKeyPublic( + context, NULL, NULL, with_free(signed_pre_key_id_str)); + stanza__set_text(context, children[0], with_free(signed_pre_key_public)); + + 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); + children[1] = stanza__iq_pubsub_publish_item_bundle_signedPreKeySignature( + context, NULL, NULL); + stanza__set_text(context, children[1], with_free(signed_pre_key_signature)); + + iks_get_identity_key_pair(&record, (signal_buffer**)&signed_pre_key, omemo); + char *identity_key = NULL; + base64_encode(signal_buffer_data(record), signal_buffer_len(record), + &identity_key); + signal_buffer_free(record); + children[2] = stanza__iq_pubsub_publish_item_bundle_identityKey( + context, NULL, NULL); + stanza__set_text(context, children[2], with_free(identity_key)); + + children[0] = stanza__iq_pubsub_publish_item_bundle( + context, NULL, children, with_noop("eu.siacs.conversations.axolotl")); + children[1] = NULL; + + children[0] = stanza__iq_pubsub_publish_item( + context, NULL, children, NULL); + + size_t bundle_node_len = strlen("eu.siacs.conversations.axolotl.bundles:") + 10; + char *bundle_node = malloc(sizeof(char) * (bundle_node_len + 1)); + snprintf(bundle_node, bundle_node_len+1, + "eu.siacs.conversations.axolotl.bundles:%u", omemo->device_id); + children[0] = stanza__iq_pubsub_publish( + context, NULL, children, with_free(bundle_node)); + + children[0] = stanza__iq_pubsub( + context, NULL, children, with_noop("http://jabber.org/protocol/pubsub")); + + parent = stanza__iq( + context, NULL, children, NULL, "announce2", from, to, "set"); + free(children); + + return parent; +} + void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo, const char *account_name) { @@ -965,30 +1738,35 @@ void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo, 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 - char *path = weechat_string_eval_expression("${weechat_data_dir}/xmpp.omemo.db", + 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, path, MDB_NOSUBDIR, 0664) != 0) + if (mdb_env_open(new_omemo->db->env, new_omemo->db_path, MDB_NOSUBDIR, 0664) != 0) { return; } - free(path); MDB_txn *parentTransaction = NULL; MDB_txn *transaction; if (mdb_txn_begin(new_omemo->db->env, parentTransaction, 0 ? MDB_RDONLY : 0, &transaction)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", weechat_prefix("error")); + return; } 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, "identity_key_%s", account_name); + 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)) { weechat_printf(NULL, "%sxmpp: failed to open lmdb database", weechat_prefix("error")); + return; } - mdb_txn_abort(transaction); + if (mdb_txn_commit(transaction)) { + weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", + weechat_prefix("error")); + return; + }; struct signal_crypto_provider crypto_provider = { .random_func = &cp_random_generator, @@ -1070,64 +1848,247 @@ void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo, signal_protocol_store_context_set_sender_key_store( new_omemo->store_context, &sender_key_store); + signal_buffer *public_data, *private_data; + iks_get_local_registration_id(new_omemo, &new_omemo->device_id); + if (!iks_get_identity_key_pair(&public_data, &private_data, new_omemo)) + { + ec_public_key *public_key = NULL; + ec_private_key *private_key = NULL; + curve_decode_point(&public_key, signal_buffer_data(public_data), + signal_buffer_len(public_data), new_omemo->context); + curve_decode_private_point(&private_key, signal_buffer_data(private_data), + signal_buffer_len(private_data), new_omemo->context); + ratchet_identity_key_pair_create(&new_omemo->identity, public_key, private_key); + } + *omemo = new_omemo; } -void omemo__serialize(struct t_omemo *omemo, char **device, - char **identity, size_t *identity_len) +void omemo__handle_devicelist(struct t_omemo *omemo, const char *jid, + xmpp_stanza_t *items) { - if (device) + (void) omemo; + + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); + if (!item) return; + xmpp_stanza_t *list = xmpp_stanza_get_child_by_name(item, "list"); + if (!list) return; + for (xmpp_stanza_t *device = xmpp_stanza_get_children(list); + device; device = xmpp_stanza_get_next(device)) { - size_t id_slen = log10(omemo->device_id) * 2; - char *id = malloc(sizeof(char) * id_slen); - snprintf(id, id_slen, "%d", omemo->device_id); + const char *name = xmpp_stanza_get_name(device); + if (weechat_strcasecmp(name, "device") != 0) + continue; + + const char *device_id = xmpp_stanza_get_id(device); + if (!device_id) + continue; - *device = id; + //weechat_printf(NULL, "omemo devicelist %s: %s%u", + // jid, weechat_color("yellow"), strtol(device_id, NULL, 10)); } - if (identity) - { - signal_buffer *buffer; - ratchet_identity_key_pair_serialize(&buffer, omemo->identity); +} - size_t key_slen = signal_buffer_len(buffer) * 2; - char *key = malloc(sizeof(char) * key_slen); - size_t length = weechat_string_base_encode(64, (char*)signal_buffer_data(buffer), - signal_buffer_len(buffer), key); +void omemo__handle_bundle(struct t_omemo *omemo, const char *jid, + uint32_t device_id, xmpp_stanza_t *items) +{ + (void) omemo; - *identity = key; - if (identity_len) - *identity_len = length; + 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"); + if (!bundle) return; + xmpp_stanza_t *signedprekey = xmpp_stanza_get_child_by_name(bundle, "signedPreKeyPublic"); + if (!signedprekey) return; + const char *signed_pre_key = xmpp_stanza_get_text(signedprekey); + if (!signed_pre_key) return; + const char *signed_pre_key_id = xmpp_stanza_get_attribute(signedprekey, "signedPreKeyId"); + if (!signed_pre_key_id) return; + xmpp_stanza_t *signature = xmpp_stanza_get_child_by_name(bundle, "signedPreKeySignature"); + if (!signature) return; + const char *key_signature = xmpp_stanza_get_text(signature); + if (!key_signature) return; + xmpp_stanza_t *identitykey = xmpp_stanza_get_child_by_name(bundle, "identityKey"); + if (!identitykey) return; + const char *identity_key = xmpp_stanza_get_text(identitykey); + if (!identity_key) return; + xmpp_stanza_t *prekeys = xmpp_stanza_get_child_by_name(bundle, "prekeys"); + if (!prekeys) return; + + 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); + prekey; prekey = xmpp_stanza_get_next(prekey)) + { + const char *name = xmpp_stanza_get_name(prekey); + if (weechat_strcasecmp(name, "preKeyPublic") != 0) + continue; + + const char *pre_key_id = xmpp_stanza_get_attribute(prekey, "preKeyId"); + if (!pre_key_id) + continue; + const char *pre_key = xmpp_stanza_get_text(prekey); + if (!pre_key) + continue; + + 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); } + //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); } -void omemo__deserialize(struct t_omemo *omemo, const char *device, - const char *identity, size_t identity_len) +char *omemo__decode(struct t_omemo *omemo, const char *jid, + xmpp_stanza_t *encrypted) { - if (device) + uint8_t *key_data = NULL, *iv_data = NULL, *payload_data = NULL; + size_t key_len = 0, iv_len = 0, payload_len = 0; + + xmpp_stanza_t *header = xmpp_stanza_get_child_by_name(encrypted, "header"); + if (!header) return NULL; + xmpp_stanza_t *iv = xmpp_stanza_get_child_by_name(header, "iv"); + if (!iv) return NULL; + const char *iv__text = xmpp_stanza_get_text(iv); + if (!iv__text) return NULL; + iv_len = base64_decode(iv__text, strlen(iv__text), &iv_data); + + char **format = weechat_string_dyn_alloc(256); + weechat_string_dyn_concat(format, "omemo msg %s:\n%s..IV: %s", -1); + for (xmpp_stanza_t *key = xmpp_stanza_get_children(header); + key; key = xmpp_stanza_get_next(key)) { - uint32_t id = device[0] ? atoi(device) : 0; + const char *name = xmpp_stanza_get_name(key); + if (weechat_strcasecmp(name, "key") != 0) + continue; + + const char *key_prekey = xmpp_stanza_get_attribute(key, "prekey"); + 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) + continue; + const char *data = xmpp_stanza_get_text(key); + 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); + } - omemo->device_id = id; + xmpp_stanza_t *payload = xmpp_stanza_get_child_by_name(encrypted, "payload"); + if (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); + weechat_string_dyn_concat(format, "\n%2$s..PL: ", -1); + weechat_string_dyn_concat(format, payload__text, -1); } - if (identity) + 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; + 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) { - uint8_t *key = malloc(sizeof(uint8_t) * identity_len); - size_t length = weechat_string_base_decode(64, identity, (char*)key); - - ratchet_identity_key_pair_deserialize(&omemo->identity, - key, length, omemo->context); + plaintext[plaintext_len] = '\0'; + return plaintext; } + return NULL; +} + +xmpp_stanza_t *omemo__encode(struct t_omemo *omemo, const char *jid, + uint32_t device_id, const char *unencrypted) +{ + 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; + aes_encrypt((uint8_t*)unencrypted, strlen(unencrypted), + &key, &iv, &tag, &tag_len, + &ciphertext, &ciphertext_len); + + uint8_t *key_and_tag = malloc(sizeof(uint8_t) * (AES_KEY_SIZE+tag_len)); + memcpy(key_and_tag, key, AES_KEY_SIZE); + free(key); + memcpy(key_and_tag+AES_KEY_SIZE, tag, tag_len); + free(tag); + char *key64 = NULL; + base64_encode(key_and_tag, AES_KEY_SIZE+tag_len, &key64); + char *iv64 = NULL; + base64_encode(iv, AES_IV_SIZE, &iv64); + free(iv); + char *ciphertext64 = NULL; + 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}; + + /* + 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); + */ + 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); + free(iv64); + free(key64); + free(ciphertext64); + return NULL; } void omemo__free(struct t_omemo *omemo) { if (omemo) { - if (omemo->context) + if (omemo->context) { signal_context_destroy(omemo->context); + signal_protocol_store_context_destroy(omemo->store_context); + } if (omemo->identity) ratchet_identity_key_pair_destroy( (signal_type_base *)omemo->identity); + free(omemo->db_path); free(omemo); } } diff --git a/omemo.h b/omemo.h index 03c29b2..bf28992 100644 --- a/omemo.h +++ b/omemo.h @@ -13,20 +13,44 @@ struct t_omemo struct signal_protocol_store_context *store_context; struct t_omemo_db *db; + char *db_path; struct ratchet_identity_key_pair *identity; uint32_t device_id; }; +struct t_omemo_bundle_req +{ + char *id; + char *jid; + char *device; + char *message_text; +}; + +struct t_omemo_devicelist_req +{ + char *id; + struct t_omemo_bundle_req bundle_req; +}; + +xmpp_stanza_t *omemo__get_bundle(xmpp_ctx_t *context, char *from, char *to, + struct t_omemo *omemo); + void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo, const char *account_name); -void omemo__serialize(struct t_omemo *omemo, char **device, - char **identity, size_t *identity_len); +void omemo__handle_devicelist(struct t_omemo *omemo, const char *jid, + xmpp_stanza_t *items); + +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, + xmpp_stanza_t *encrypted); -void omemo__deserialize(struct t_omemo *omemo, const char *device, - const char *identity, size_t identity_len); +xmpp_stanza_t *omemo__encode(struct t_omemo *omemo, const char *jid, + uint32_t device_id, const char *unencrypted); void omemo__free(struct t_omemo *omemo); diff --git a/plugin.c b/plugin.c index d8668a7..c9ae1ff 100644 --- a/plugin.c +++ b/plugin.c @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include #include diff --git a/user.c b/user.c index a2af4f2..ba1546b 100644 --- a/user.c +++ b/user.c @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include #include @@ -191,6 +192,7 @@ struct t_user *user__new(struct t_account *account, new_user->profile.email = NULL; new_user->profile.role = NULL; new_user->profile.pgp_id = NULL; + new_user->profile.omemo = 0; new_user->updated = 0; new_user->is_away = 0; diff --git a/user.h b/user.h index 9c153c5..ccc9537 100644 --- a/user.h +++ b/user.h @@ -16,6 +16,7 @@ struct t_user_profile char *role; char *affiliation; char *pgp_id; + int omemo; }; struct t_user diff --git a/xmpp/iq.c b/xmpp/iq.c index 91c70e5..53e437a 100644 --- a/xmpp/iq.c +++ b/xmpp/iq.c @@ -193,7 +193,7 @@ xmpp_stanza_t *stanza__iq_pubsub_publish_item_list(xmpp_ctx_t *context, xmpp_sta } xmpp_stanza_t *stanza__iq_pubsub_publish_item_list_device(xmpp_ctx_t *context, xmpp_stanza_t *base, - struct t_string *id) + struct t_string *id, struct t_string *label) { xmpp_stanza_t *parent = base; @@ -210,6 +210,13 @@ xmpp_stanza_t *stanza__iq_pubsub_publish_item_list_device(xmpp_ctx_t *context, x free(id); } + if (label) + { + xmpp_stanza_set_attribute(parent, "label", label->value); + label->finalize(label); + free(label); + } + return parent; } diff --git a/xmpp/stanza.h b/xmpp/stanza.h index e63f7de..9858541 100644 --- a/xmpp/stanza.h +++ b/xmpp/stanza.h @@ -49,6 +49,22 @@ static inline struct t_string *with_xmpp_free(char *value, xmpp_ctx_t *pointer) return string; } +static inline void stanza__set_text(xmpp_ctx_t *context, xmpp_stanza_t *parent, + struct t_string *value) +{ + xmpp_stanza_t *text = xmpp_stanza_new(context); + + if (value) + { + xmpp_stanza_set_text(text, value->value); + xmpp_stanza_add_child(parent, text); + value->finalize(value); + free(value); + } + + xmpp_stanza_release(text); +} + xmpp_stanza_t *stanza__presence(xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, const char *ns, char *from, char *to, const char *type); @@ -72,7 +88,7 @@ xmpp_stanza_t *stanza__iq_pubsub_publish_item_list(xmpp_ctx_t *context, xmpp_sta xmpp_stanza_t **children, struct t_string *ns); xmpp_stanza_t *stanza__iq_pubsub_publish_item_list_device(xmpp_ctx_t *context, xmpp_stanza_t *base, - struct t_string *id); + struct t_string *id, struct t_string *label); xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle(xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, struct t_string *ns);