From fdc5f25f2d1ba2a1b08a5c5b0ca41ed6395a1e76 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 21 Feb 2019 19:44:01 +0140 Subject: [PATCH] Add devicelist and bundle publication --- src/omemo/omemo.c | 134 +++++++++++++++++++++++++++++++++++------ src/omemo/omemo.h | 10 +++ src/xmpp/omemo.c | 44 +++++++++++++- src/xmpp/omemo.h | 2 + src/xmpp/session.c | 6 +- src/xmpp/stanza.c | 147 ++++++++++++++++++++++++++++++++++++++++++++- src/xmpp/stanza.h | 5 +- 7 files changed, 323 insertions(+), 25 deletions(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 1b2998b0..080baf1b 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1,26 +1,41 @@ +#include + +#include #include +#include #include +#include #include "config/account.h" -#include "ui/ui.h" -#include "omemo/omemo.h" +#include "log.h" #include "omemo/crypto.h" +#include "omemo/omemo.h" +#include "ui/ui.h" +#include "xmpp/omemo.h" + +static gboolean loaded; static void lock(void *user_data); static void unlock(void *user_data); -static void omemo_load_crypto_materials(ProfAccount *account); struct omemo_context_t { pthread_mutexattr_t attr; pthread_mutex_t lock; + signal_context *signal; + uint32_t device_id; + GList *device_list; + ratchet_identity_key_pair *identity_key_pair; + uint32_t registration_id; + signal_protocol_key_helper_pre_key_list_node *pre_keys_head; + session_signed_pre_key *signed_pre_key; }; +static omemo_context omemo_ctx; + void omemo_init(void) { log_info("Initialising OMEMO"); - signal_context *signal_ctx; - omemo_context *ctx = malloc(sizeof(omemo_context)); signal_crypto_provider crypto_provider = { .random_func = omemo_random_func, .hmac_sha256_init_func = omemo_hmac_sha256_init_func, @@ -40,35 +55,116 @@ omemo_init(void) cons_show("Error initializing OMEMO crypto"); } - pthread_mutexattr_init(&ctx->attr); - pthread_mutexattr_settype(&ctx->attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&ctx->lock, &ctx->attr); + pthread_mutexattr_init(&omemo_ctx.attr); + pthread_mutexattr_settype(&omemo_ctx.attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&omemo_ctx.lock, &omemo_ctx.attr); - if (signal_context_create(&signal_ctx, ctx) != 0) { + if (signal_context_create(&omemo_ctx.signal, &omemo_ctx) != 0) { cons_show("Error initializing OMEMO context"); return; } - if (signal_context_set_crypto_provider(signal_ctx, &crypto_provider) != 0) { + if (signal_context_set_crypto_provider(omemo_ctx.signal, &crypto_provider) != 0) { cons_show("Error initializing OMEMO crypto"); return; } - signal_context_set_locking_functions(signal_ctx, lock, unlock); + signal_context_set_locking_functions(omemo_ctx.signal, lock, unlock); + + loaded = FALSE; + omemo_ctx.device_list = NULL; } void omemo_generate_crypto_materials(ProfAccount *account) { - ratchet_identity_key_pair *identity_key_pair; - uint32_t registration_id; - signal_protocol_key_helper_pre_key_list_node *pre_keys_head; - session_signed_pre_key *signed_pre_key; + omemo_ctx.device_id = randombytes_uniform(0x80000000); + omemo_ctx.device_list = g_list_append(omemo_ctx.device_list, GINT_TO_POINTER(omemo_ctx.device_id)); + signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal); + signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal); + signal_protocol_key_helper_generate_pre_keys(&omemo_ctx.pre_keys_head, randombytes_random(), 100, omemo_ctx.signal); - signal_protocol_key_helper_generate_identity_key_pair(&identity_key_pair, global_context); - signal_protocol_key_helper_generate_registration_id(®istration_id, 0, global_context); - signal_protocol_key_helper_generate_pre_keys(&pre_keys_head, start_id, 100, global_context); - signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, identity_key_pair, 5, timestamp, global_context); + struct timeval tv; + gettimeofday(&tv, NULL); + unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; + signal_protocol_key_helper_generate_signed_pre_key(&omemo_ctx.signed_pre_key, omemo_ctx.identity_key_pair, 5, timestamp, omemo_ctx.signal); + + loaded = TRUE; + + omemo_devicelist_publish(); + omemo_bundle_publish(); +} + +void +omemo_start_session(ProfAccount *account, char *barejid) +{ + +} + +gboolean +omemo_loaded(void) +{ + return loaded; +} + +GList * const +omemo_device_list(void) +{ + return omemo_ctx.device_list; +} + +uint32_t +omemo_device_id(void) +{ + return omemo_ctx.device_id; +} + +void +omemo_identity_key(unsigned char **output, size_t *length) +{ + signal_buffer *buffer = NULL; + ec_public_key_serialize(&buffer, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair)); + *length = signal_buffer_len(buffer); + *output = malloc(*length); + memcpy(*output, signal_buffer_const_data(buffer), *length); + signal_buffer_free(buffer); +} + +void +omemo_signed_prekey(unsigned char **output, size_t *length) +{ + signal_buffer *buffer = NULL; + ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(omemo_ctx.signed_pre_key))); + *length = signal_buffer_len(buffer); + *output = malloc(*length); + memcpy(*output, signal_buffer_const_data(buffer), *length); + signal_buffer_free(buffer); +} + +void +omemo_signed_prekey_signature(unsigned char **output, size_t *length) +{ + *length = session_signed_pre_key_get_signature_len(omemo_ctx.signed_pre_key); + *output = malloc(*length); + memcpy(*output, session_signed_pre_key_get_signature(omemo_ctx.signed_pre_key), *length); +} + +void +omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths) +{ + signal_protocol_key_helper_pre_key_list_node *p; + for (p = omemo_ctx.pre_keys_head; p != NULL; p = signal_protocol_key_helper_key_list_next(p)) { + session_pre_key *prekey = signal_protocol_key_helper_key_list_element(p); + signal_buffer *buffer = NULL; + ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_pre_key_get_key_pair(prekey))); + size_t length = signal_buffer_len(buffer); + unsigned char *prekey_value = malloc(length); + memcpy(prekey_value, signal_buffer_const_data(buffer), length); + signal_buffer_free(buffer); + *prekeys = g_list_append(*prekeys, prekey_value); + *ids = g_list_append(*ids, GINT_TO_POINTER(session_pre_key_get_id(prekey))); + *lengths = g_list_append(*lengths, GINT_TO_POINTER(length)); + } } static void diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 825529b1..20fd5d5d 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -4,3 +4,13 @@ typedef struct omemo_context_t omemo_context; void omemo_init(void); void omemo_generate_crypto_materials(ProfAccount *account); + +GList * const omemo_device_list(void); +uint32_t omemo_device_id(void); +void omemo_identity_key(unsigned char **output, size_t *length); +void omemo_signed_prekey(unsigned char **output, size_t *length); +void omemo_signed_prekey_signature(unsigned char **output, size_t *length); +void omemo_prekeys(GList ** const prekeys, GList ** const ids, GList ** const lengths); + +void omemo_start_session(ProfAccount *account, char *barejid); +gboolean omemo_loaded(void); diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 42b18c6b..a979ec90 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -2,14 +2,54 @@ #include "xmpp/iq.h" #include "xmpp/stanza.h" +#include "omemo/omemo.h" + void -omemo_devicelist_publish(void) +omemo_devicelist_subscribe(void) { xmpp_ctx_t * const ctx = connection_get_ctx(); char *barejid = xmpp_jid_bare(ctx, session_get_account_name()); - xmpp_stanza_t *iq = stanza_create_omemo_devicelist_subscription(ctx, barejid); + xmpp_stanza_t *iq = stanza_create_omemo_devicelist_subscribe(ctx, barejid); iq_send_stanza(iq); xmpp_stanza_release(iq); free(barejid); } + +void +omemo_devicelist_publish(void) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, omemo_device_list()); + iq_send_stanza(iq); + xmpp_stanza_release(iq); +} + +void +omemo_bundle_publish(void) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + unsigned char *identity_key = NULL; + size_t identity_key_length; + unsigned char *signed_prekey = NULL; + size_t signed_prekey_length; + unsigned char *signed_prekey_signature = NULL; + size_t signed_prekey_signature_length; + GList *prekeys = NULL, *ids = NULL, *lengths = NULL; + + omemo_identity_key(&identity_key, &identity_key_length); + omemo_signed_prekey(&signed_prekey, &signed_prekey_length); + omemo_signed_prekey_signature(&signed_prekey_signature, &signed_prekey_signature_length); + omemo_prekeys(&prekeys, &ids, &lengths); + + xmpp_stanza_t *iq = stanza_create_omemo_bundle_publish(ctx, omemo_device_id(), + identity_key, identity_key_length, signed_prekey, signed_prekey_length, + signed_prekey_signature, signed_prekey_signature_length, + prekeys, ids, lengths); + iq_send_stanza(iq); + xmpp_stanza_release(iq); + + free(identity_key); + free(signed_prekey); + free(signed_prekey_signature); +} diff --git a/src/xmpp/omemo.h b/src/xmpp/omemo.h index 413aa563..eff3eee3 100644 --- a/src/xmpp/omemo.h +++ b/src/xmpp/omemo.h @@ -1 +1,3 @@ +void omemo_devicelist_subscribe(void); void omemo_devicelist_publish(void); +void omemo_bundle_publish(void); diff --git a/src/xmpp/session.c b/src/xmpp/session.c index ee836090..ef53ad6f 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -61,6 +61,7 @@ #include "xmpp/jid.h" #ifdef HAVE_OMEMO +#include "omemo/omemo.h" #include "xmpp/omemo.h" #endif @@ -318,7 +319,10 @@ session_login_success(gboolean secured) bookmark_request(); blocking_request(); #ifdef HAVE_OMEMO - omemo_devicelist_publish(); + omemo_devicelist_subscribe(); + if (omemo_loaded()) { + omemo_devicelist_publish(); + } #endif // items discovery diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index ffd0c053..e6400b0b 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2093,7 +2093,7 @@ stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, } xmpp_stanza_t* -stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char *const jid) +stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid) { char *id = connection_create_stanza_id("omemo_devicelist_subscribe"); xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); @@ -2104,7 +2104,7 @@ stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char * xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); xmpp_stanza_t *subscribe = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(subscribe, "subscribe"); + xmpp_stanza_set_name(subscribe, STANZA_NAME_SUBSCRIBE); xmpp_stanza_set_attribute(subscribe, "node", "eu.siacs.conversations.axolotl.devicelist"); xmpp_stanza_set_attribute(subscribe, "jid", jid); @@ -2117,6 +2117,149 @@ stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char * return iq; } +xmpp_stanza_t* +stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids) +{ + char *id = connection_create_stanza_id("omemo_devicelist_publish"); + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + free(id); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *publish = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); + xmpp_stanza_set_attribute(publish, "node", "eu.siacs.conversations.axolotl.devicelist"); + + xmpp_stanza_t *item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + xmpp_stanza_set_attribute(item, "id", "current"); + + xmpp_stanza_t *list = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(list, "list"); + xmpp_stanza_set_ns(list, "eu.siacs.conversations.axolotl"); + + GList *i; + for (i = ids; i != NULL; i = i->next) { + xmpp_stanza_t *device = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(device, "device"); + char *id = g_strdup_printf("%d", GPOINTER_TO_INT(i->data)); + xmpp_stanza_set_attribute(device, "id", id); + g_free(id); + + xmpp_stanza_add_child(list, device); + xmpp_stanza_release(device); + } + + xmpp_stanza_add_child(item, list); + xmpp_stanza_add_child(publish, item); + xmpp_stanza_add_child(pubsub, publish); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(list); + xmpp_stanza_release(item); + xmpp_stanza_release(publish); + xmpp_stanza_release(pubsub); + + return iq; +} + +xmpp_stanza_t* +stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, + const unsigned char * const identity_key, size_t identity_key_length, + const unsigned char * const signed_prekey, size_t signed_prekey_length, + const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, + GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length) +{ + char *id = connection_create_stanza_id("omemo_bundle_publish"); + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + free(id); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *publish = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); + char *node = g_strdup_printf("%s:%d", "eu.siacs.conversations.axolotl.bundles", device_id); + xmpp_stanza_set_attribute(publish, "node", node); + g_free(node); + + xmpp_stanza_t *item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + xmpp_stanza_set_attribute(item, "id", "current"); + + xmpp_stanza_t *bundle = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(bundle, "bundle"); + xmpp_stanza_set_ns(bundle, "eu.siacs.conversations.axolotl"); + + xmpp_stanza_t *signed_prekey_public_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(signed_prekey_public_stanza , "signedPreKeyPublic"); + xmpp_stanza_set_attribute(signed_prekey_public_stanza, "signedPreKeyId", "1"); + + xmpp_stanza_t *signed_prekey_public_stanza_text= xmpp_stanza_new(ctx); + xmpp_stanza_set_text(signed_prekey_public_stanza_text, g_base64_encode(signed_prekey, signed_prekey_length)); + xmpp_stanza_add_child(signed_prekey_public_stanza, signed_prekey_public_stanza_text); + xmpp_stanza_release(signed_prekey_public_stanza_text); + + xmpp_stanza_t *signed_prekey_signature_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(signed_prekey_signature_stanza , "signedPreKeySignature"); + + xmpp_stanza_t *signed_prekey_signature_stanza_text= xmpp_stanza_new(ctx); + xmpp_stanza_set_text(signed_prekey_signature_stanza_text, g_base64_encode(signed_prekey_signature, signed_prekey_signature_length)); + xmpp_stanza_add_child(signed_prekey_signature_stanza, signed_prekey_signature_stanza_text); + xmpp_stanza_release(signed_prekey_signature_stanza_text); + + xmpp_stanza_t *identity_key_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(identity_key_stanza , "identityKey"); + + xmpp_stanza_t *identity_key_stanza_text= xmpp_stanza_new(ctx); + xmpp_stanza_set_text(identity_key_stanza_text, g_base64_encode(identity_key, identity_key_length)); + xmpp_stanza_add_child(identity_key_stanza, identity_key_stanza_text); + xmpp_stanza_release(identity_key_stanza_text); + + xmpp_stanza_t *prekeys_stanza = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(prekeys_stanza, "prekeys"); + + GList *p, *i, *l; + for (p = prekeys, i = prekeys_id, l = prekeys_length; p != NULL; p = p->next, i = i->next, l = l->next) { + xmpp_stanza_t *prekey = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(prekey, "preKeyPublic"); + char *id = g_strdup_printf("%d", GPOINTER_TO_INT(i->data)); + xmpp_stanza_set_attribute(prekey, "id", id); + g_free(id); + + xmpp_stanza_t *prekey_text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(prekey_text, g_base64_encode(p->data, GPOINTER_TO_INT(l->data))); + + xmpp_stanza_add_child(prekey, prekey_text); + xmpp_stanza_add_child(prekeys_stanza, prekey); + xmpp_stanza_release(prekey_text); + xmpp_stanza_release(prekey); + } + + xmpp_stanza_add_child(bundle, signed_prekey_public_stanza); + xmpp_stanza_add_child(bundle, signed_prekey_signature_stanza); + xmpp_stanza_add_child(bundle, identity_key_stanza); + xmpp_stanza_add_child(bundle, prekeys_stanza); + xmpp_stanza_add_child(item, bundle); + xmpp_stanza_add_child(publish, item); + xmpp_stanza_add_child(pubsub, publish); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(signed_prekey_public_stanza); + xmpp_stanza_release(signed_prekey_signature_stanza); + xmpp_stanza_release(identity_key_stanza); + xmpp_stanza_release(prekeys_stanza); + xmpp_stanza_release(bundle); + xmpp_stanza_release(item); + xmpp_stanza_release(publish); + xmpp_stanza_release(pubsub); + + return iq; +} + static void _stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index ce655a93..9cca027d 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -82,6 +82,7 @@ #define STANZA_NAME_PUBSUB "pubsub" #define STANZA_NAME_PUBLISH "publish" #define STANZA_NAME_PUBLISH_OPTIONS "publish-options" +#define STANZA_NAME_SUBSCRIBE "subscribe" #define STANZA_NAME_FIELD "field" #define STANZA_NAME_STORAGE "storage" #define STANZA_NAME_NICK "nick" @@ -284,7 +285,9 @@ xmpp_stanza_t* stanza_create_room_kick_iq(xmpp_ctx_t *const ctx, const char *con xmpp_stanza_t* stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node); xmpp_stanza_t* stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, const char *const node, const char *const sessionid, DataForm *form); -xmpp_stanza_t* stanza_create_omemo_devicelist_pubsub_subscription(xmpp_ctx_t *ctx, const char *const jid); +xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid); +xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids); +xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length); int stanza_get_idle_time(xmpp_stanza_t *const stanza);