From f9216fddb106d46bac2d13d9dfe175b4a475a789 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Mon, 25 Feb 2019 18:07:11 +0140 Subject: [PATCH] Add signal store backend and OMEMO start command --- Makefile.am | 3 +- src/command/cmd_defs.c | 3 +- src/command/cmd_funcs.c | 53 ++++++++ src/command/cmd_funcs.h | 1 + src/omemo/omemo.c | 80 +++++++++++- src/omemo/omemo.h | 6 +- src/omemo/store.c | 267 ++++++++++++++++++++++++++++++++++++++++ src/omemo/store.h | 34 +++++ src/xmpp/omemo.c | 74 ++++++++++- src/xmpp/omemo.h | 4 + src/xmpp/stanza.c | 25 ++++ src/xmpp/stanza.h | 2 + 12 files changed, 545 insertions(+), 7 deletions(-) create mode 100644 src/omemo/store.c create mode 100644 src/omemo/store.h diff --git a/Makefile.am b/Makefile.am index d5e92bce..ca60ac95 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,6 +71,7 @@ unittest_sources = \ src/pgp/gpg.h \ src/omemo/omemo.h \ src/omemo/crypto.h \ + src/omemo/store.h \ src/command/cmd_defs.h src/command/cmd_defs.c \ src/command/cmd_funcs.h src/command/cmd_funcs.c \ src/command/cmd_ac.h src/command/cmd_ac.c \ @@ -172,7 +173,7 @@ otr4_sources = \ omemo_sources = \ src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c \ - src/xmpp/omemo.h src/xmpp/omemo.c + src/omemo/store.h src/omemo/store.c src/xmpp/omemo.h src/xmpp/omemo.c if BUILD_PYTHON_API core_sources += $(python_sources) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index af30db34..265af119 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2333,7 +2333,8 @@ static struct cmd_t command_defs[] = { "/omemo", parse_args, 1, 3, NULL, CMD_SUBFUNCS( - { "gen", cmd_omemo_gen }) + { "gen", cmd_omemo_gen }, + { "start", cmd_omemo_start }) CMD_NOMAINFUNC CMD_TAGS( CMD_TAG_CHAT, diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index ab438c7f..66715d20 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7904,3 +7904,56 @@ cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args) return TRUE; #endif } + +gboolean +cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OMEMO information."); + return TRUE; + } + + // recipient supplied + if (args[1]) { + char *contact = args[1]; + char *barejid = roster_barejid_from_name(contact); + if (barejid == NULL) { + barejid = contact; + } + + ProfChatWin *chatwin = wins_get_chat(barejid); + if (!chatwin) { + chatwin = chatwin_new(barejid); + } + ui_focus_win((ProfWin*)chatwin); + + if (chatwin->pgp_send) { + win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session."); + return TRUE; + } + + if (chatwin->is_otr) { + win_println(window, THEME_DEFAULT, '!', "You must disable OTR encryption before starting an OMEMO session."); + return TRUE; + } + + if (chatwin->is_omemo) { + win_println(window, THEME_DEFAULT, '!', "You are already in an OMEMO session."); + return TRUE; + } + + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo init'"); + return TRUE; + } + + omemo_start_session(barejid); + } + + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 650c9eaf..f39e8c17 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -215,5 +215,6 @@ gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args) gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index e4926baa..32fdce42 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -4,10 +4,12 @@ #include #include #include +#include #include #include "config/account.h" #include "log.h" +#include "omemo/store.h" #include "omemo/crypto.h" #include "omemo/omemo.h" #include "ui/ui.h" @@ -30,6 +32,11 @@ struct omemo_context_t { uint32_t registration_id; signal_protocol_key_helper_pre_key_list_node *pre_keys_head; session_signed_pre_key *signed_pre_key; + signal_protocol_store_context *store; + GHashTable *session_store; + GHashTable *pre_key_store; + GHashTable *signed_pre_key_store; + identity_key_store_t identity_key_store; }; static omemo_context omemo_ctx; @@ -73,6 +80,53 @@ omemo_init(void) signal_context_set_locking_functions(omemo_ctx.signal, lock, unlock); + signal_protocol_store_context_create(&omemo_ctx.store, omemo_ctx.signal); + + omemo_ctx.session_store = session_store_new(); + signal_protocol_session_store session_store = { + .load_session_func = load_session, + .get_sub_device_sessions_func = get_sub_device_sessions, + .store_session_func = store_session, + .contains_session_func = contains_session, + .delete_session_func = delete_session, + .delete_all_sessions_func = delete_all_sessions, + .destroy_func = NULL, + .user_data = omemo_ctx.session_store + }; + signal_protocol_store_context_set_session_store(omemo_ctx.store, &session_store); + + omemo_ctx.pre_key_store = pre_key_store_new(); + signal_protocol_pre_key_store pre_key_store = { + .load_pre_key = load_pre_key, + .store_pre_key = store_pre_key, + .contains_pre_key = contains_pre_key, + .remove_pre_key = remove_pre_key, + .destroy_func = NULL, + .user_data = omemo_ctx.pre_key_store + }; + signal_protocol_store_context_set_pre_key_store(omemo_ctx.store, &pre_key_store); + + omemo_ctx.signed_pre_key_store = signed_pre_key_store_new(); + signal_protocol_signed_pre_key_store signed_pre_key_store = { + .load_signed_pre_key = load_signed_pre_key, + .store_signed_pre_key = store_signed_pre_key, + .contains_signed_pre_key = contains_signed_pre_key, + .remove_signed_pre_key = remove_signed_pre_key, + .destroy_func = NULL, + .user_data = omemo_ctx.pre_key_store + }; + signal_protocol_store_context_set_signed_pre_key_store(omemo_ctx.store, &signed_pre_key_store); + + identity_key_store_new(&omemo_ctx.identity_key_store); + signal_protocol_identity_key_store identity_key_store = { + .get_identity_key_pair = get_identity_key_pair, + .get_local_registration_id = get_local_registration_id, + .save_identity = save_identity, + .is_trusted_identity = is_trusted_identity, + }; + signal_protocol_store_context_set_identity_key_store(omemo_ctx.store, &identity_key_store); + + loaded = FALSE; omemo_ctx.device_list = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_list_free); } @@ -103,9 +157,19 @@ omemo_generate_crypto_materials(ProfAccount *account) } void -omemo_start_session(ProfAccount *account, char *barejid) +omemo_start_session(const char *const barejid) { + GList *device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid); + if (!device_list) { + omemo_devicelist_request(barejid); + /* TODO handle response */ + return; + } + GList *device_id; + for (device_id = device_list; device_id != NULL; device_id = device_id->next) { + omemo_bundle_request(barejid, GPOINTER_TO_INT(device_id->data), omemo_start_device_session_handle_bundle, free, strdup(barejid)); + } } gboolean @@ -151,7 +215,7 @@ omemo_signed_prekey_signature(unsigned char **output, size_t *length) } void -omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths) +omemo_prekeys(GList **prekeys, GList **ids, GList **lengths) { signal_protocol_key_helper_pre_key_list_node *p; for (p = omemo_ctx.pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { @@ -184,6 +248,18 @@ omemo_set_device_list(const char *const jid, GList * device_list) g_hash_table_insert(omemo_ctx.device_list, strdup(jid), device_list); } +void +omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const prekey, size_t prekey_len) +{ + signal_protocol_address address = { + jid, strlen(jid), device_id + }; + + session_builder *builder; + session_builder_create(&builder, omemo_ctx.store, &address, omemo_ctx.signal); + //session_builder_process_pre_key_bundle(builder, prekey); +} + static void lock(void *user_data) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 31d942b8..26bd6e65 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -11,8 +11,10 @@ uint32_t omemo_device_id(void); void omemo_identity_key(unsigned char **output, size_t *length); void omemo_signed_prekey(unsigned char **output, size_t *length); void omemo_signed_prekey_signature(unsigned char **output, size_t *length); -void omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths); +void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths); void omemo_set_device_list(const char *const jid, GList * device_list); -void omemo_start_session(ProfAccount *account, char *barejid); +void omemo_start_session(const char *const barejid); +void omemo_start_device_session(const char *const jid, uint32_t device_id, const unsigned char *const prekey, size_t prekey_len); + gboolean omemo_loaded(void); diff --git a/src/omemo/store.c b/src/omemo/store.c new file mode 100644 index 00000000..384eb9ce --- /dev/null +++ b/src/omemo/store.c @@ -0,0 +1,267 @@ +#include +#include + +#include "omemo/store.h" + +GHashTable * +session_store_new(void) +{ + return g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); +} + +GHashTable * +pre_key_store_new(void) +{ + return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); +} + +GHashTable * +signed_pre_key_store_new(void) +{ + return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); +} + +void +identity_key_store_new(identity_key_store_t *identity_key_store) +{ + identity_key_store->identity_key_store = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)signal_buffer_free); + identity_key_store->private = NULL; + identity_key_store->public = NULL; +} + +int +load_session(signal_buffer **record, const signal_protocol_address *address, + void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, address->name); + if (!device_store) { + *record = NULL; + return SG_SUCCESS; + } + + signal_buffer *original = g_hash_table_lookup(device_store, GINT_TO_POINTER(address->device_id)); + *record = signal_buffer_copy(original); + return SG_SUCCESS; +} + +int +get_sub_device_sessions(signal_int_list **sessions, const char *name, + size_t name_len, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + GHashTableIter iter; + gpointer key, value; + + device_store = g_hash_table_lookup(session_store, name); + if (!device_store) { + return SG_SUCCESS; + } + + *sessions = signal_int_list_alloc(); + g_hash_table_iter_init(&iter, device_store); + while (g_hash_table_iter_next(&iter, &key, &value)) { + signal_int_list_push_back(*sessions, GPOINTER_TO_INT(key)); + } + + + return SG_SUCCESS; +} + +int +store_session(const signal_protocol_address *address, uint8_t *record, + size_t record_len, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, (void *)address->name); + if (!device_store) { + device_store = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free); + g_hash_table_insert(session_store, strdup(address->name), device_store); + } + + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(device_store, GINT_TO_POINTER(address->device_id), buffer); + return SG_SUCCESS; +} + +int +contains_session(const signal_protocol_address *address, void *user_data) +{ + signal_buffer *record; + load_session(&record, address, user_data); + return record != NULL; +} + +int +delete_session(const signal_protocol_address *address, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, address->name); + if (!device_store) { + return SG_SUCCESS; + } + + return g_hash_table_remove(device_store, GINT_TO_POINTER(address->device_id)); +} + +int +delete_all_sessions(const char *name, size_t name_len, void *user_data) +{ + GHashTable *session_store = (GHashTable *)user_data; + GHashTable *device_store = NULL; + + device_store = g_hash_table_lookup(session_store, name); + if (!device_store) { + return SG_SUCCESS; + } + + guint len = g_hash_table_size(device_store); + g_hash_table_remove_all(device_store); + return len; +} + +int +load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data) +{ + GHashTable *pre_key_store = (GHashTable *)user_data; + + *record = g_hash_table_lookup(pre_key_store, GINT_TO_POINTER(pre_key_id)); + return SG_SUCCESS; +} + +int +store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, + void *user_data) +{ + GHashTable *pre_key_store = (GHashTable *)user_data; + + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(pre_key_store, GINT_TO_POINTER(pre_key_id), buffer); + return SG_SUCCESS; +} + +int +contains_pre_key(uint32_t pre_key_id, void *user_data) +{ + signal_buffer *record; + load_pre_key(&record, pre_key_id, user_data); + + return record != NULL; +} + +int +remove_pre_key(uint32_t pre_key_id, void *user_data) +{ + GHashTable *pre_key_store = (GHashTable *)user_data; + + return g_hash_table_remove(pre_key_store, GINT_TO_POINTER(pre_key_id)); +} + +int +load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, + void *user_data) +{ + GHashTable *signed_pre_key_store = (GHashTable *)user_data; + + *record = g_hash_table_lookup(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); + return SG_SUCCESS; +} + +int +store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, + size_t record_len, void *user_data) +{ + GHashTable *signed_pre_key_store = (GHashTable *)user_data; + + signal_buffer *buffer = signal_buffer_create(record, record_len); + g_hash_table_insert(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id), buffer); + return SG_SUCCESS; +} + +int +contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) +{ + signal_buffer *record; + load_signed_pre_key(&record, signed_pre_key_id, user_data); + + return record != NULL; +} + +int +remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data) +{ + GHashTable *signed_pre_key_store = (GHashTable *)user_data; + + return g_hash_table_remove(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)); +} + +int +get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, + void *user_data) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + + *public_data = identity_key_store->public; + *private_data = identity_key_store->private; + + return SG_SUCCESS; +} + +int +get_local_registration_id(void *user_data, uint32_t *registration_id) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + + *registration_id = identity_key_store->registration_id; + + return SG_SUCCESS; +} + +int +save_identity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len, void *user_data) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + char *node = g_strdup_printf("%s:%d", address->name, address->device_id); + + signal_buffer *buffer = signal_buffer_create(key_data, key_len); + g_hash_table_insert(identity_key_store->identity_key_store, node, buffer); + + return SG_SUCCESS; +} + +int +is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len, void *user_data) +{ + identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data; + char *node = g_strdup_printf("%s:%d", address->name, address->device_id); + + signal_buffer *buffer = signal_buffer_create(key_data, key_len); + signal_buffer *original = g_hash_table_lookup(identity_key_store->identity_key_store, node); + + return original == NULL || signal_buffer_compare(buffer, original); +} + +int +store_sender_key(const signal_protocol_sender_key_name *sender_key_name, + uint8_t *record, size_t record_len, uint8_t *user_record, + size_t user_record_len, void *user_data) +{ + return SG_SUCCESS; +} + +int +load_sender_key(signal_buffer **record, signal_buffer **user_record, + const signal_protocol_sender_key_name *sender_key_name, + void *user_data) +{ + return SG_SUCCESS; +} diff --git a/src/omemo/store.h b/src/omemo/store.h new file mode 100644 index 00000000..e99c514b --- /dev/null +++ b/src/omemo/store.h @@ -0,0 +1,34 @@ +#include + +typedef struct { + signal_buffer *public; + signal_buffer *private; + uint32_t registration_id; + GHashTable * identity_key_store; +} identity_key_store_t; + +GHashTable * session_store_new(void); +GHashTable * pre_key_store_new(void); +GHashTable * signed_pre_key_store_new(void); +void identity_key_store_new(identity_key_store_t *identity_key_store); + +int load_session(signal_buffer **record, const signal_protocol_address *address, void *user_data); +int get_sub_device_sessions(signal_int_list **sessions, const char *name, size_t name_len, void *user_data); +int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, void *user_data); +int contains_session(const signal_protocol_address *address, void *user_data); +int delete_session(const signal_protocol_address *address, void *user_data); +int delete_all_sessions(const char *name, size_t name_len, void *user_data); +int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data); +int store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data); +int contains_pre_key(uint32_t pre_key_id, void *user_data); +int remove_pre_key(uint32_t pre_key_id, void *user_data); +int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data); +int store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data); +int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data); +int remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data); +int get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data); +int get_local_registration_id(void *user_data, uint32_t *registration_id); +int save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data); +int is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data); +int store_sender_key(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data); +int load_sender_key(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name, void *user_data); diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 5418b1b4..f4641212 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -71,8 +71,80 @@ omemo_bundle_publish(void) } void -omemo_bundles_fetch(const char * const jid) +omemo_bundle_request(const char * const jid, uint32_t device_id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata) { + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *id = connection_create_stanza_id("devicelist_request"); + + xmpp_stanza_t *iq = stanza_create_omemo_bundle_request(ctx, id, jid, device_id); + iq_id_handler_add(id, func, free_func, userdata); + + iq_send_stanza(iq); + + free(id); + xmpp_stanza_release(iq); +} + +int +omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *const userdata) +{ + const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + if (!from) { + return 1; + } + + if (!g_strcmp0(from, userdata)) { + return 1; + } + + xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB); + if (!pubsub) { + return 1; + } + + xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(pubsub, "items"); + if (!items) { + return 1; + } + const char *node = xmpp_stanza_get_attribute(items, "node"); + char *device_id_str = strstr(node, ":"); + if (!device_id_str) { + return 1; + } + + uint32_t device_id = strtoul(device_id_str, NULL, 10); + + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); + if (!item) { + return 1; + } + + xmpp_stanza_t *bundle = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO); + if (!bundle) { + return 1; + } + + xmpp_stanza_t *prekeys = xmpp_stanza_get_child_by_name(bundle, "prekeys"); + if (!prekeys) { + return 1; + } + + /* Should be random */ + xmpp_stanza_t *prekey = xmpp_stanza_get_children(prekeys); + if (!prekey) { + return 1; + } + + xmpp_stanza_t *prekey_text = xmpp_stanza_get_children(prekey); + if (!prekey_text) { + return 1; + } + + size_t prekey_len; + unsigned char *prekey_raw = g_base64_decode(xmpp_stanza_get_text(prekey_text), &prekey_len); + + omemo_start_device_session(from, device_id, prekey_raw, prekey_len); + return 1; } static int diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index b125f76a..c384c4f0 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -1,6 +1,10 @@ #include +#include "xmpp/iq.h" + void omemo_devicelist_subscribe(void); void omemo_devicelist_publish(GList *device_list); void omemo_devicelist_request(const char * const jid); void omemo_bundle_publish(void); +void omemo_bundle_request(const char * const jid, uint32_t device_id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata); +int omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *const userdata); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index d5993377..302258ec 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2284,6 +2284,31 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, return iq; } +xmpp_stanza_t* +stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, uint32_t device_id) +{ + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id); + xmpp_stanza_set_to(iq, jid); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *items = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(items, "items"); + char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, device_id); + xmpp_stanza_set_attribute(items, "node", node); + g_free(node); + + xmpp_stanza_add_child(pubsub, items); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(items); + xmpp_stanza_release(pubsub); + + return iq; +} + static void _stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 919379bb..bc157e46 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -193,6 +193,7 @@ #define STANZA_NS_COMMAND "http://jabber.org/protocol/commands" #define STANZA_NS_OMEMO "eu.siacs.conversations.axolotl" #define STANZA_NS_OMEMO_DEVICELIST "eu.siacs.conversations.axolotl.devicelist" +#define STANZA_NS_OMEMO_BUNDLES "eu.siacs.conversations.axolotl.bundles" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" @@ -292,6 +293,7 @@ xmpp_stanza_t* stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const cha xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid); xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids); xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length); +xmpp_stanza_t* stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, uint32_t device_id); int stanza_get_idle_time(xmpp_stanza_t *const stanza);