2019-06-17 04:47:56 -04:00
|
|
|
/*
|
|
|
|
* omemo.c
|
2019-11-13 06:11:05 -05:00
|
|
|
* vim: expandtab:ts=4:sts=4:sw=4
|
2019-06-17 04:47:56 -04:00
|
|
|
*
|
|
|
|
* Copyright (C) 2019 Paul Fariello <paul@fariello.eu>
|
2021-01-08 10:36:30 -05:00
|
|
|
* Copyright (C) 2019 - 2021 Michael Vetter <jubalh@iodoru.org>
|
2019-06-17 04:47:56 -04:00
|
|
|
*
|
|
|
|
* This file is part of Profanity.
|
|
|
|
*
|
|
|
|
* Profanity is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Profanity is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* In addition, as a special exception, the copyright holders give permission to
|
|
|
|
* link the code of portions of this program with the OpenSSL library under
|
|
|
|
* certain conditions as described in each individual source file, and
|
|
|
|
* distribute linked combinations including the two.
|
|
|
|
*
|
|
|
|
* You must obey the GNU General Public License in all respects for all of the
|
|
|
|
* code used other than OpenSSL. If you modify file(s) with this exception, you
|
|
|
|
* may extend this exception to your version of the file(s), but you are not
|
|
|
|
* obligated to do so. If you do not wish to do so, delete this exception
|
|
|
|
* statement from your version. If you delete this exception statement from all
|
|
|
|
* source files in the program, then also delete it here.
|
|
|
|
*
|
|
|
|
*/
|
2021-03-26 14:01:56 -04:00
|
|
|
#include "config.h"
|
|
|
|
|
2020-07-07 03:43:28 -04:00
|
|
|
#include <sys/time.h>
|
2020-07-07 07:53:30 -04:00
|
|
|
#include <sys/stat.h>
|
2019-02-21 13:04:01 -05:00
|
|
|
|
2019-03-18 01:31:19 -04:00
|
|
|
#include <assert.h>
|
2019-03-01 12:41:20 -05:00
|
|
|
#include <errno.h>
|
2019-02-21 13:04:01 -05:00
|
|
|
#include <glib.h>
|
2019-02-19 12:58:40 -05:00
|
|
|
#include <pthread.h>
|
2019-02-21 13:04:01 -05:00
|
|
|
#include <signal/key_helper.h>
|
2019-02-26 13:53:06 -05:00
|
|
|
#include <signal/protocol.h>
|
2020-07-07 07:53:30 -04:00
|
|
|
#include <signal/signal_protocol.h>
|
2019-02-25 11:27:11 -05:00
|
|
|
#include <signal/session_builder.h>
|
2019-02-26 13:53:06 -05:00
|
|
|
#include <signal/session_cipher.h>
|
2019-02-18 23:44:47 -05:00
|
|
|
|
|
|
|
#include "config/account.h"
|
2019-03-07 13:05:11 -05:00
|
|
|
#include "config/files.h"
|
2019-04-15 16:09:47 -04:00
|
|
|
#include "config/preferences.h"
|
2019-02-21 13:04:01 -05:00
|
|
|
#include "log.h"
|
2019-02-19 12:38:15 -05:00
|
|
|
#include "omemo/crypto.h"
|
2019-02-21 13:04:01 -05:00
|
|
|
#include "omemo/omemo.h"
|
2019-03-07 13:05:11 -05:00
|
|
|
#include "omemo/store.h"
|
2019-02-21 13:04:01 -05:00
|
|
|
#include "ui/ui.h"
|
2019-03-07 13:05:11 -05:00
|
|
|
#include "ui/window_list.h"
|
2019-02-22 13:17:26 -05:00
|
|
|
#include "xmpp/connection.h"
|
2019-03-14 15:45:47 -04:00
|
|
|
#include "xmpp/muc.h"
|
2019-02-21 13:04:01 -05:00
|
|
|
#include "xmpp/omemo.h"
|
2019-04-10 11:11:15 -04:00
|
|
|
#include "xmpp/roster_list.h"
|
2019-03-07 13:05:11 -05:00
|
|
|
#include "xmpp/xmpp.h"
|
2019-02-21 13:04:01 -05:00
|
|
|
|
2020-07-20 16:49:50 -04:00
|
|
|
#define AESGCM_URL_NONCE_LEN (2 * OMEMO_AESGCM_NONCE_LENGTH)
|
|
|
|
#define AESGCM_URL_KEY_LEN (2 * OMEMO_AESGCM_KEY_LENGTH)
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
static gboolean loaded;
|
2019-02-18 23:44:47 -05:00
|
|
|
|
2019-04-01 15:12:00 -04:00
|
|
|
static void _generate_pre_keys(int count);
|
|
|
|
static void _generate_signed_pre_key(void);
|
2019-04-12 11:37:28 -04:00
|
|
|
static gboolean _load_identity(void);
|
2019-04-08 21:01:02 -04:00
|
|
|
static void _load_trust(void);
|
2019-04-01 15:16:39 -04:00
|
|
|
static void _load_sessions(void);
|
2019-06-07 15:00:32 -04:00
|
|
|
static void _load_known_devices(void);
|
2020-07-07 08:18:57 -04:00
|
|
|
static void _lock(void* user_data);
|
|
|
|
static void _unlock(void* user_data);
|
|
|
|
static void _omemo_log(int level, const char* message, size_t len, void* user_data);
|
|
|
|
static gboolean _handle_own_device_list(const char* const jid, GList* device_list);
|
|
|
|
static gboolean _handle_device_list_start_session(const char* const jid, GList* device_list);
|
|
|
|
static char* _omemo_fingerprint(ec_public_key* identity, gboolean formatted);
|
|
|
|
static unsigned char* _omemo_fingerprint_decode(const char* const fingerprint, size_t* len);
|
|
|
|
static char* _omemo_unformat_fingerprint(const char* const fingerprint_formatted);
|
|
|
|
static void _cache_device_identity(const char* const jid, uint32_t device_id, ec_public_key* identity);
|
|
|
|
static void _g_hash_table_free(GHashTable* hash_table);
|
|
|
|
|
|
|
|
typedef gboolean (*OmemoDeviceListHandler)(const char* const jid, GList* device_list);
|
|
|
|
|
|
|
|
struct omemo_context_t
|
|
|
|
{
|
2019-02-19 12:58:40 -05:00
|
|
|
pthread_mutexattr_t attr;
|
|
|
|
pthread_mutex_t lock;
|
2020-07-07 08:18:57 -04:00
|
|
|
signal_context* signal;
|
2019-02-21 13:04:01 -05:00
|
|
|
uint32_t device_id;
|
2020-07-07 08:18:57 -04:00
|
|
|
GHashTable* device_list;
|
|
|
|
GHashTable* device_list_handler;
|
|
|
|
ratchet_identity_key_pair* identity_key_pair;
|
2019-02-21 13:04:01 -05:00
|
|
|
uint32_t registration_id;
|
2019-03-05 13:21:15 -05:00
|
|
|
uint32_t signed_pre_key_id;
|
2020-07-07 08:18:57 -04:00
|
|
|
signal_protocol_store_context* store;
|
|
|
|
GHashTable* session_store;
|
|
|
|
GHashTable* pre_key_store;
|
|
|
|
GHashTable* signed_pre_key_store;
|
2019-02-25 11:27:11 -05:00
|
|
|
identity_key_store_t identity_key_store;
|
2020-07-07 08:18:57 -04:00
|
|
|
GString* identity_filename;
|
|
|
|
GKeyFile* identity_keyfile;
|
|
|
|
GString* trust_filename;
|
|
|
|
GKeyFile* trust_keyfile;
|
|
|
|
GString* sessions_filename;
|
|
|
|
GKeyFile* sessions_keyfile;
|
|
|
|
GHashTable* known_devices;
|
|
|
|
GString* known_devices_filename;
|
|
|
|
GKeyFile* known_devices_keyfile;
|
|
|
|
GHashTable* fingerprint_ac;
|
2019-02-19 12:58:40 -05:00
|
|
|
};
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
static omemo_context omemo_ctx;
|
|
|
|
|
2019-02-18 23:44:47 -05:00
|
|
|
void
|
2019-02-19 13:23:50 -05:00
|
|
|
omemo_init(void)
|
2019-02-18 23:44:47 -05:00
|
|
|
{
|
2021-03-06 13:51:34 -05:00
|
|
|
log_info("[OMEMO] initialising");
|
2019-02-19 12:38:15 -05:00
|
|
|
if (omemo_crypto_init() != 0) {
|
2020-02-21 04:30:09 -05:00
|
|
|
cons_show("Error initializing OMEMO crypto: gcry_check_version() failed");
|
2019-02-19 12:38:15 -05:00
|
|
|
}
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
pthread_mutexattr_init(&omemo_ctx.attr);
|
|
|
|
pthread_mutexattr_settype(&omemo_ctx.attr, PTHREAD_MUTEX_RECURSIVE);
|
|
|
|
pthread_mutex_init(&omemo_ctx.lock, &omemo_ctx.attr);
|
2019-02-19 12:58:40 -05:00
|
|
|
|
2020-01-30 17:44:43 -05:00
|
|
|
omemo_ctx.fingerprint_ac = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)autocomplete_free);
|
2019-03-27 12:15:59 -04:00
|
|
|
}
|
|
|
|
|
2019-07-04 04:30:56 -04:00
|
|
|
void
|
|
|
|
omemo_close(void)
|
|
|
|
{
|
|
|
|
if (omemo_ctx.fingerprint_ac) {
|
2020-01-30 17:44:43 -05:00
|
|
|
g_hash_table_destroy(omemo_ctx.fingerprint_ac);
|
2019-07-04 04:30:56 -04:00
|
|
|
omemo_ctx.fingerprint_ac = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 12:15:59 -04:00
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_on_connect(ProfAccount* account)
|
2019-03-27 12:15:59 -04:00
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
GError* error = NULL;
|
2019-03-27 12:15:59 -04:00
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
if (signal_context_create(&omemo_ctx.signal, &omemo_ctx) != 0) {
|
2019-02-19 13:23:50 -05:00
|
|
|
cons_show("Error initializing OMEMO context");
|
2019-02-19 12:38:15 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-01 15:16:39 -04:00
|
|
|
if (signal_context_set_log_function(omemo_ctx.signal, _omemo_log) != 0) {
|
2019-02-26 13:53:06 -05:00
|
|
|
cons_show("Error initializing OMEMO log");
|
|
|
|
}
|
|
|
|
|
2019-03-27 12:15:59 -04:00
|
|
|
signal_crypto_provider crypto_provider = {
|
|
|
|
.random_func = omemo_random_func,
|
|
|
|
.hmac_sha256_init_func = omemo_hmac_sha256_init_func,
|
|
|
|
.hmac_sha256_update_func = omemo_hmac_sha256_update_func,
|
|
|
|
.hmac_sha256_final_func = omemo_hmac_sha256_final_func,
|
|
|
|
.hmac_sha256_cleanup_func = omemo_hmac_sha256_cleanup_func,
|
|
|
|
.sha512_digest_init_func = omemo_sha512_digest_init_func,
|
|
|
|
.sha512_digest_update_func = omemo_sha512_digest_update_func,
|
|
|
|
.sha512_digest_final_func = omemo_sha512_digest_final_func,
|
|
|
|
.sha512_digest_cleanup_func = omemo_sha512_digest_cleanup_func,
|
|
|
|
.encrypt_func = omemo_encrypt_func,
|
|
|
|
.decrypt_func = omemo_decrypt_func,
|
|
|
|
.user_data = NULL
|
|
|
|
};
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
if (signal_context_set_crypto_provider(omemo_ctx.signal, &crypto_provider) != 0) {
|
2020-02-21 04:30:09 -05:00
|
|
|
cons_show("Error initializing OMEMO crypto: unable to set crypto provider");
|
2019-02-19 12:38:15 -05:00
|
|
|
return;
|
|
|
|
}
|
2019-02-19 12:58:40 -05:00
|
|
|
|
2019-04-01 15:16:39 -04:00
|
|
|
signal_context_set_locking_functions(omemo_ctx.signal, _lock, _unlock);
|
2019-02-21 13:04:01 -05:00
|
|
|
|
2019-02-25 11:27:11 -05:00
|
|
|
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,
|
2019-03-05 13:21:15 -05:00
|
|
|
.user_data = omemo_ctx.signed_pre_key_store
|
2019-02-25 11:27:11 -05:00
|
|
|
};
|
|
|
|
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,
|
2019-02-26 13:53:06 -05:00
|
|
|
.destroy_func = NULL,
|
|
|
|
.user_data = &omemo_ctx.identity_key_store
|
2019-02-25 11:27:11 -05:00
|
|
|
};
|
|
|
|
signal_protocol_store_context_set_identity_key_store(omemo_ctx.store, &identity_key_store);
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
loaded = FALSE;
|
2019-02-22 13:17:26 -05:00
|
|
|
omemo_ctx.device_list = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_list_free);
|
2019-03-04 00:58:32 -05:00
|
|
|
omemo_ctx.device_list_handler = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
|
2019-04-01 15:16:39 -04:00
|
|
|
omemo_ctx.known_devices = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_g_hash_table_free);
|
2019-03-25 13:27:36 -04:00
|
|
|
|
2020-07-07 08:18:57 -04:00
|
|
|
gchar* omemo_dir = files_get_account_data_path(DIR_OMEMO, account->jid);
|
2019-03-01 12:41:20 -05:00
|
|
|
|
2020-07-01 04:05:45 -04:00
|
|
|
omemo_ctx.identity_filename = g_string_new(omemo_dir);
|
|
|
|
g_string_append(omemo_ctx.identity_filename, "/identity.txt");
|
|
|
|
omemo_ctx.trust_filename = g_string_new(omemo_dir);
|
|
|
|
g_string_append(omemo_ctx.trust_filename, "/trust.txt");
|
|
|
|
omemo_ctx.sessions_filename = g_string_new(omemo_dir);
|
|
|
|
g_string_append(omemo_ctx.sessions_filename, "/sessions.txt");
|
|
|
|
omemo_ctx.known_devices_filename = g_string_new(omemo_dir);
|
|
|
|
g_string_append(omemo_ctx.known_devices_filename, "/known_devices.txt");
|
2019-03-07 01:20:29 -05:00
|
|
|
|
2019-03-01 12:41:20 -05:00
|
|
|
errno = 0;
|
2020-07-01 04:05:45 -04:00
|
|
|
int res = g_mkdir_with_parents(omemo_dir, S_IRWXU);
|
2019-03-01 12:41:20 -05:00
|
|
|
if (res == -1) {
|
2020-10-14 03:52:26 -04:00
|
|
|
const char* errmsg = strerror(errno);
|
2019-03-01 12:41:20 -05:00
|
|
|
if (errmsg) {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_error("[OMEMO] error creating directory: %s, %s", omemo_dir, errmsg);
|
2019-03-01 12:41:20 -05:00
|
|
|
} else {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_error("[OMEMO] creating directory: %s", omemo_dir);
|
2019-03-01 12:41:20 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 04:05:45 -04:00
|
|
|
g_free(omemo_dir);
|
2019-03-21 19:03:16 -04:00
|
|
|
|
2019-03-18 01:31:19 -04:00
|
|
|
omemo_devicelist_subscribe();
|
|
|
|
|
2019-03-01 12:41:20 -05:00
|
|
|
omemo_ctx.identity_keyfile = g_key_file_new();
|
2019-04-08 21:01:02 -04:00
|
|
|
omemo_ctx.trust_keyfile = g_key_file_new();
|
2019-03-18 01:31:19 -04:00
|
|
|
omemo_ctx.sessions_keyfile = g_key_file_new();
|
2019-06-07 15:00:32 -04:00
|
|
|
omemo_ctx.known_devices_keyfile = g_key_file_new();
|
2019-03-18 01:31:19 -04:00
|
|
|
|
|
|
|
if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) {
|
2019-04-12 11:37:28 -04:00
|
|
|
if (!_load_identity()) {
|
|
|
|
return;
|
|
|
|
}
|
2019-03-18 01:31:19 -04:00
|
|
|
} else if (error->code != G_FILE_ERROR_NOENT) {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_warning("[OMEMO] error loading identity from: %s, %s", omemo_ctx.identity_filename->str, error->message);
|
2020-07-10 08:34:11 -04:00
|
|
|
g_error_free(error);
|
2019-03-18 01:31:19 -04:00
|
|
|
return;
|
2019-03-01 12:41:20 -05:00
|
|
|
}
|
2019-03-07 01:20:29 -05:00
|
|
|
|
2019-04-08 21:01:02 -04:00
|
|
|
error = NULL;
|
|
|
|
if (g_key_file_load_from_file(omemo_ctx.trust_keyfile, omemo_ctx.trust_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) {
|
|
|
|
_load_trust();
|
|
|
|
} else if (error->code != G_FILE_ERROR_NOENT) {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_warning("[OMEMO] error loading trust from: %s, %s", omemo_ctx.trust_filename->str, error->message);
|
2020-07-10 08:34:11 -04:00
|
|
|
g_error_free(error);
|
2021-07-22 02:29:05 -04:00
|
|
|
} else {
|
|
|
|
log_warning("[OMEMO] no such file: %s", omemo_ctx.trust_filename->str);
|
|
|
|
g_error_free(error);
|
2019-04-08 21:01:02 -04:00
|
|
|
}
|
|
|
|
|
2019-03-18 01:31:19 -04:00
|
|
|
error = NULL;
|
|
|
|
if (g_key_file_load_from_file(omemo_ctx.sessions_keyfile, omemo_ctx.sessions_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) {
|
2019-04-01 15:16:39 -04:00
|
|
|
_load_sessions();
|
2019-03-18 01:31:19 -04:00
|
|
|
} else if (error->code != G_FILE_ERROR_NOENT) {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_warning("[OMEMO] error loading sessions from: %s, %s", omemo_ctx.sessions_filename->str, error->message);
|
2020-07-10 08:34:11 -04:00
|
|
|
g_error_free(error);
|
2021-07-22 02:29:05 -04:00
|
|
|
} else {
|
|
|
|
log_warning("[OMEMO] no such file: %s", omemo_ctx.trust_filename->str);
|
|
|
|
g_error_free(error);
|
2019-03-07 01:20:29 -05:00
|
|
|
}
|
2019-06-07 15:00:32 -04:00
|
|
|
|
|
|
|
error = NULL;
|
|
|
|
if (g_key_file_load_from_file(omemo_ctx.known_devices_keyfile, omemo_ctx.known_devices_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) {
|
|
|
|
_load_known_devices();
|
|
|
|
} else if (error->code != G_FILE_ERROR_NOENT) {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_warning("[OMEMO] error loading known devices from: %s, %s", omemo_ctx.known_devices_filename->str, error->message);
|
2020-07-10 08:34:11 -04:00
|
|
|
g_error_free(error);
|
2021-07-22 02:29:05 -04:00
|
|
|
} else {
|
|
|
|
log_warning("[OMEMO] no such file: %s", omemo_ctx.trust_filename->str);
|
|
|
|
g_error_free(error);
|
2019-06-07 15:00:32 -04:00
|
|
|
}
|
2019-03-01 12:41:20 -05:00
|
|
|
}
|
|
|
|
|
2019-03-21 19:03:16 -04:00
|
|
|
void
|
|
|
|
omemo_on_disconnect(void)
|
|
|
|
{
|
2019-04-12 01:04:50 -04:00
|
|
|
if (!loaded) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-01 15:16:39 -04:00
|
|
|
_g_hash_table_free(omemo_ctx.signed_pre_key_store);
|
|
|
|
_g_hash_table_free(omemo_ctx.pre_key_store);
|
2019-07-04 04:47:50 -04:00
|
|
|
_g_hash_table_free(omemo_ctx.device_list_handler);
|
2019-03-21 19:03:16 -04:00
|
|
|
|
|
|
|
g_string_free(omemo_ctx.identity_filename, TRUE);
|
|
|
|
g_key_file_free(omemo_ctx.identity_keyfile);
|
2019-04-08 21:01:02 -04:00
|
|
|
g_string_free(omemo_ctx.trust_filename, TRUE);
|
|
|
|
g_key_file_free(omemo_ctx.trust_keyfile);
|
2019-03-21 19:03:16 -04:00
|
|
|
g_string_free(omemo_ctx.sessions_filename, TRUE);
|
|
|
|
g_key_file_free(omemo_ctx.sessions_keyfile);
|
2019-06-19 17:15:24 -04:00
|
|
|
_g_hash_table_free(omemo_ctx.session_store);
|
2019-06-07 15:00:32 -04:00
|
|
|
g_string_free(omemo_ctx.known_devices_filename, TRUE);
|
|
|
|
g_key_file_free(omemo_ctx.known_devices_keyfile);
|
2019-03-21 19:03:16 -04:00
|
|
|
}
|
|
|
|
|
2019-03-01 12:41:20 -05:00
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_generate_crypto_materials(ProfAccount* account)
|
2019-03-01 12:41:20 -05:00
|
|
|
{
|
|
|
|
if (loaded) {
|
|
|
|
return;
|
|
|
|
}
|
2019-02-22 13:17:26 -05:00
|
|
|
|
2019-08-23 04:14:04 -04:00
|
|
|
log_info("Generate long term OMEMO cryptography materials");
|
2019-04-01 07:00:19 -04:00
|
|
|
|
|
|
|
/* Device ID */
|
2019-03-06 12:17:11 -05:00
|
|
|
gcry_randomize(&omemo_ctx.device_id, 4, GCRY_VERY_STRONG_RANDOM);
|
|
|
|
omemo_ctx.device_id &= 0x7fffffff;
|
2019-04-01 07:00:19 -04:00
|
|
|
g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_DEVICE_ID, omemo_ctx.device_id);
|
2021-03-06 13:51:34 -05:00
|
|
|
log_info("[OMEMO] device id: %d", omemo_ctx.device_id);
|
2019-02-22 13:17:26 -05:00
|
|
|
|
2019-04-01 07:00:19 -04:00
|
|
|
/* Identity key */
|
2019-02-21 13:04:01 -05:00
|
|
|
signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal);
|
2019-03-01 12:41:20 -05:00
|
|
|
|
|
|
|
ec_public_key_serialize(&omemo_ctx.identity_key_store.public, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair));
|
2020-07-07 08:18:57 -04:00
|
|
|
char* identity_key_public = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.public), signal_buffer_len(omemo_ctx.identity_key_store.public));
|
2019-03-01 12:41:20 -05:00
|
|
|
g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, identity_key_public);
|
|
|
|
g_free(identity_key_public);
|
2019-04-01 07:00:19 -04:00
|
|
|
|
|
|
|
ec_private_key_serialize(&omemo_ctx.identity_key_store.private, ratchet_identity_key_pair_get_private(omemo_ctx.identity_key_pair));
|
2020-07-07 08:18:57 -04:00
|
|
|
char* identity_key_private = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.private), signal_buffer_len(omemo_ctx.identity_key_store.private));
|
2019-03-01 12:41:20 -05:00
|
|
|
g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, identity_key_private);
|
|
|
|
g_free(identity_key_private);
|
|
|
|
|
2019-04-01 07:00:19 -04:00
|
|
|
/* Registration ID */
|
|
|
|
signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal);
|
|
|
|
g_key_file_set_uint64(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_REGISTRATION_ID, omemo_ctx.registration_id);
|
2019-03-01 12:41:20 -05:00
|
|
|
|
2019-04-01 07:00:19 -04:00
|
|
|
/* Pre keys */
|
2019-04-01 15:12:00 -04:00
|
|
|
_generate_pre_keys(100);
|
2019-03-21 19:03:16 -04:00
|
|
|
|
2019-04-01 07:00:19 -04:00
|
|
|
/* Signed pre key */
|
2019-04-01 15:12:00 -04:00
|
|
|
_generate_signed_pre_key();
|
2019-02-21 13:04:01 -05:00
|
|
|
|
2019-04-01 07:00:19 -04:00
|
|
|
omemo_identity_keyfile_save();
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
loaded = TRUE;
|
|
|
|
|
2019-04-08 13:09:25 -04:00
|
|
|
omemo_publish_crypto_materials();
|
2019-04-10 11:11:15 -04:00
|
|
|
omemo_start_sessions();
|
2019-04-01 07:00:19 -04:00
|
|
|
}
|
|
|
|
|
2019-04-08 13:09:25 -04:00
|
|
|
void
|
|
|
|
omemo_publish_crypto_materials(void)
|
2019-04-01 07:00:19 -04:00
|
|
|
{
|
2021-06-05 13:12:42 -04:00
|
|
|
log_debug("[OMEMO] publish crypto materials");
|
|
|
|
|
2019-04-08 13:09:25 -04:00
|
|
|
if (loaded != TRUE) {
|
2021-03-06 13:51:34 -05:00
|
|
|
cons_show("OMEMO: cannot publish crypto materials before they are generated");
|
|
|
|
log_error("[OMEMO] cannot publish crypto materials before they are generated");
|
2019-04-08 13:09:25 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-07 08:18:57 -04:00
|
|
|
char* barejid = connection_get_barejid();
|
2019-04-08 13:09:25 -04:00
|
|
|
|
2019-02-24 23:59:41 -05:00
|
|
|
/* Ensure we get our current device list, and it gets updated with our
|
|
|
|
* device_id */
|
2020-05-25 07:04:19 -04:00
|
|
|
g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), _handle_own_device_list);
|
|
|
|
omemo_devicelist_request(barejid);
|
2019-03-01 12:41:20 -05:00
|
|
|
|
2019-03-28 18:30:28 -04:00
|
|
|
omemo_bundle_publish(true);
|
2019-04-08 13:09:25 -04:00
|
|
|
|
2020-05-25 07:04:19 -04:00
|
|
|
free(barejid);
|
2019-02-21 13:04:01 -05:00
|
|
|
}
|
|
|
|
|
2019-04-10 11:11:15 -04:00
|
|
|
void
|
|
|
|
omemo_start_sessions(void)
|
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
GSList* contacts = roster_get_contacts(ROSTER_ORD_NAME);
|
2019-04-10 11:11:15 -04:00
|
|
|
if (contacts) {
|
2020-07-07 08:18:57 -04:00
|
|
|
GSList* curr;
|
|
|
|
for (curr = contacts; curr != NULL; curr = g_slist_next(curr)) {
|
2019-04-10 11:11:15 -04:00
|
|
|
PContact contact = curr->data;
|
2020-07-07 08:18:57 -04:00
|
|
|
const char* jid = p_contact_barejid(contact);
|
2019-04-10 11:11:15 -04:00
|
|
|
omemo_start_session(jid);
|
|
|
|
}
|
2019-06-19 17:18:45 -04:00
|
|
|
g_slist_free(contacts);
|
2019-04-10 11:11:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_start_session(const char* const barejid)
|
2019-02-21 13:04:01 -05:00
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
if (omemo_loaded()) {
|
2021-05-28 11:45:15 -04:00
|
|
|
log_debug("[OMEMO] start session with %s", barejid);
|
2020-07-07 08:18:57 -04:00
|
|
|
GList* device_list = g_hash_table_lookup(omemo_ctx.device_list, barejid);
|
2020-06-04 13:57:07 -04:00
|
|
|
if (!device_list) {
|
2021-05-28 11:45:15 -04:00
|
|
|
log_debug("[OMEMO] missing device list for %s", barejid);
|
2021-06-05 13:12:42 -04:00
|
|
|
// Own devices are handled by _handle_own_device_list
|
|
|
|
// We won't add _handle_device_list_start_session for ourself
|
|
|
|
char* mybarejid = connection_get_barejid();
|
|
|
|
if( g_strcmp0(mybarejid, barejid ) != 0 ) {
|
|
|
|
g_hash_table_insert(omemo_ctx.device_list_handler, strdup(barejid), _handle_device_list_start_session);
|
|
|
|
}
|
|
|
|
free(mybarejid);
|
2020-06-04 13:57:07 -04:00
|
|
|
omemo_devicelist_request(barejid);
|
|
|
|
return;
|
|
|
|
}
|
2019-02-21 13:04:01 -05:00
|
|
|
|
2020-07-07 08:18:57 -04:00
|
|
|
GList* device_id;
|
2020-06-04 13:57:07 -04:00
|
|
|
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));
|
|
|
|
}
|
2019-02-25 11:27:11 -05:00
|
|
|
}
|
2019-02-21 13:04:01 -05:00
|
|
|
}
|
2019-02-19 13:23:50 -05:00
|
|
|
|
2019-03-15 13:52:28 -04:00
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_start_muc_sessions(const char* const roomjid)
|
2019-03-15 13:52:28 -04:00
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
GList* members = muc_members(roomjid);
|
|
|
|
GList* iter;
|
2020-01-20 08:28:13 -05:00
|
|
|
for (iter = members; iter != NULL; iter = iter->next) {
|
2020-07-07 08:18:57 -04:00
|
|
|
Jid* jid = jid_create(iter->data);
|
2020-01-20 08:28:13 -05:00
|
|
|
omemo_start_session(jid->barejid);
|
|
|
|
jid_destroy(jid);
|
2019-03-15 13:52:28 -04:00
|
|
|
}
|
2020-01-20 08:28:13 -05:00
|
|
|
g_list_free(members);
|
2019-03-15 13:52:28 -04:00
|
|
|
}
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
gboolean
|
|
|
|
omemo_loaded(void)
|
|
|
|
{
|
|
|
|
return loaded;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
omemo_device_id(void)
|
|
|
|
{
|
|
|
|
return omemo_ctx.device_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_identity_key(unsigned char** output, size_t* length)
|
2019-02-21 13:04:01 -05:00
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
signal_buffer* buffer = NULL;
|
2019-02-21 13:04:01 -05:00
|
|
|
ec_public_key_serialize(&buffer, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair));
|
|
|
|
*length = signal_buffer_len(buffer);
|
|
|
|
*output = malloc(*length);
|
2019-02-26 13:53:06 -05:00
|
|
|
memcpy(*output, signal_buffer_data(buffer), *length);
|
2019-02-21 13:04:01 -05:00
|
|
|
signal_buffer_free(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_signed_prekey(unsigned char** output, size_t* length)
|
2019-02-21 13:04:01 -05:00
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
session_signed_pre_key* signed_pre_key;
|
|
|
|
signal_buffer* buffer = NULL;
|
2019-03-05 13:21:15 -05:00
|
|
|
|
2019-04-01 07:00:19 -04:00
|
|
|
if (signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id) != SG_SUCCESS) {
|
|
|
|
*output = NULL;
|
|
|
|
*length = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-05 13:21:15 -05:00
|
|
|
ec_public_key_serialize(&buffer, ec_key_pair_get_public(session_signed_pre_key_get_key_pair(signed_pre_key)));
|
2019-03-21 19:03:16 -04:00
|
|
|
SIGNAL_UNREF(signed_pre_key);
|
2019-02-21 13:04:01 -05:00
|
|
|
*length = signal_buffer_len(buffer);
|
|
|
|
*output = malloc(*length);
|
2019-02-26 13:53:06 -05:00
|
|
|
memcpy(*output, signal_buffer_data(buffer), *length);
|
2019-02-21 13:04:01 -05:00
|
|
|
signal_buffer_free(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_signed_prekey_signature(unsigned char** output, size_t* length)
|
2019-02-21 13:04:01 -05:00
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
session_signed_pre_key* signed_pre_key;
|
2019-03-05 13:21:15 -05:00
|
|
|
|
2019-04-01 07:00:19 -04:00
|
|
|
if (signal_protocol_signed_pre_key_load_key(omemo_ctx.store, &signed_pre_key, omemo_ctx.signed_pre_key_id) != SG_SUCCESS) {
|
|
|
|
*output = NULL;
|
|
|
|
*length = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-05 13:21:15 -05:00
|
|
|
*length = session_signed_pre_key_get_signature_len(signed_pre_key);
|
2019-02-21 13:04:01 -05:00
|
|
|
*output = malloc(*length);
|
2019-03-05 13:21:15 -05:00
|
|
|
memcpy(*output, session_signed_pre_key_get_signature(signed_pre_key), *length);
|
2019-03-21 19:03:16 -04:00
|
|
|
SIGNAL_UNREF(signed_pre_key);
|
2019-02-21 13:04:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_prekeys(GList** prekeys, GList** ids, GList** lengths)
|
2019-02-21 13:04:01 -05:00
|
|
|
{
|
2019-03-05 13:21:15 -05:00
|
|
|
GHashTableIter iter;
|
|
|
|
gpointer id;
|
|
|
|
|
|
|
|
g_hash_table_iter_init(&iter, omemo_ctx.pre_key_store);
|
|
|
|
while (g_hash_table_iter_next(&iter, &id, NULL)) {
|
2020-07-07 08:18:57 -04:00
|
|
|
session_pre_key* pre_key;
|
2019-03-05 13:21:15 -05:00
|
|
|
int ret;
|
|
|
|
ret = signal_protocol_pre_key_load_key(omemo_ctx.store, &pre_key, GPOINTER_TO_INT(id));
|
|
|
|
if (ret != SG_SUCCESS) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-07-07 08:18:57 -04:00
|
|
|
signal_buffer* public_key;
|
2019-03-05 13:21:15 -05:00
|
|
|
ec_public_key_serialize(&public_key, ec_key_pair_get_public(session_pre_key_get_key_pair(pre_key)));
|
2019-03-21 19:03:16 -04:00
|
|
|
SIGNAL_UNREF(pre_key);
|
2019-03-05 13:21:15 -05:00
|
|
|
size_t length = signal_buffer_len(public_key);
|
2020-07-07 08:18:57 -04:00
|
|
|
unsigned char* prekey_value = malloc(length);
|
2019-03-05 13:21:15 -05:00
|
|
|
memcpy(prekey_value, signal_buffer_data(public_key), length);
|
2019-03-21 19:03:16 -04:00
|
|
|
signal_buffer_free(public_key);
|
|
|
|
|
2019-02-21 13:04:01 -05:00
|
|
|
*prekeys = g_list_append(*prekeys, prekey_value);
|
2019-03-05 13:21:15 -05:00
|
|
|
*ids = g_list_append(*ids, GINT_TO_POINTER(id));
|
2019-02-21 13:04:01 -05:00
|
|
|
*lengths = g_list_append(*lengths, GINT_TO_POINTER(length));
|
|
|
|
}
|
2019-02-19 13:23:50 -05:00
|
|
|
}
|
|
|
|
|
2019-02-22 13:17:26 -05:00
|
|
|
void
|
2020-07-07 08:18:57 -04:00
|
|
|
omemo_set_device_list(const char* const from, GList* device_list)
|
2019-02-22 13:17:26 -05:00
|
|
|
{
|
2021-06-05 13:12:42 -04:00
|
|
|
log_debug("[OMEMO] Setting device list for %s", from);
|
2020-07-07 08:18:57 -04:00
|
|
|
Jid* jid;
|
2019-03-14 14:03:14 -04:00
|
|
|
if (from) {
|
|
|
|
jid = jid_create(from);
|
|
|
|
} else {
|
|
|
|
jid = jid_create(connection_get_fulljid());
|
|
|
|
}
|
2019-02-24 23:24:03 -05:00
|
|
|
|
2019-03-13 14:29:45 -04:00
|
|
|
g_hash_table_insert(omemo_ctx.device_list, strdup(jid->barejid), device_list);
|
2019-03-04 00:58:32 -05:00
|
|
|
|
2019-03-13 14:29:45 -04:00
|
|
|
OmemoDeviceListHandler handler = g_hash_table_lookup(omemo_ctx.device_list_handler, jid->barejid);
|
2019-03-04 00:58:32 -05:00
|
|
|
if (handler) {
|
2019-03-13 14:29:45 -04:00
|
|
|
gboolean keep = handler(jid->barejid, device_list);
|
2019-03-04 00:58:32 -05:00
|
|
|
if (!keep) {
|
2019-03-13 14:29:45 -04:00
|
|
|
g_hash_table_remove(omemo_ctx.device_list_handler, jid->barejid);
|
2019-02-24 23:24:03 -05:00
|
|
|
}
|
2021-06-05 13:12:42 -04:00
|
|
|
} else {
|
|
|
|
log_debug("[OMEMO] No Device List Handler for %s", from);
|
2019-02-24 23:24:03 -05:00
|
|
|
}
|
|
|
|
|
2021-04-17 07:28:54 -04:00
|
|
|
// OMEMO trustmode ToFu
|
|
|
|
if (g_strcmp0(prefs_get_string(PREF_OMEMO_TRUST_MODE), "firstusage") == 0) {
|
2021-05-28 11:45:15 -04:00
|
|
|
log_debug("[OMEMO] Checking firstusage state for %s", jid->barejid);
|
2021-04-17 07:28:54 -04:00
|
|
|
GHashTable* trusted = g_hash_table_lookup(omemo_ctx.identity_key_store.trusted, jid->barejid);
|
|
|
|
if (trusted) {
|
|
|
|
if (g_hash_table_size(trusted) > 0) {
|
2021-05-28 11:45:15 -04:00
|
|
|
log_debug("[OMEMO] Found trusted device for %s - skip firstusage", jid->barejid);
|
2021-04-17 07:28:54 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (device_list) {
|
|
|
|
cons_show("OMEMO: No trusted devices found for %s", jid->barejid);
|
|
|
|
GList* device_id;
|
|
|
|
for (device_id = device_list; device_id != NULL; device_id = device_id->next) {
|
|
|
|
GHashTable* known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid->barejid);
|
|
|
|
if (known_identities) {
|
|
|
|
GList* fp = NULL;
|
|
|
|
for (fp = g_hash_table_get_keys(known_identities); fp != NULL; fp = fp->next) {
|
|
|
|
if (device_id->data == g_hash_table_lookup(known_identities, fp->data)) {
|
|
|
|
cons_show("OMEMO: Adding firstusage trust for %s device %d - Fingerprint %s", jid->barejid, device_id->data, omemo_format_fingerprint(fp->data));
|
|
|
|
omemo_trust(jid->barejid, omemo_format_fingerprint(fp->data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-14 15:45:47 -04:00
|
|
|
jid_destroy(jid);
|
2019-02-22 13:17:26 -05:00
|
|
|
}
|
|
|
|
|
2020-07-07 08:18:57 -04:00
|
|
|
GKeyFile*
|
2019-03-07 12:58:15 -05:00
|
|
|
omemo_identity_keyfile(void)
|
|
|
|
{
|
|
|
|
return omemo_ctx.identity_keyfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
omemo_identity_keyfile_save(void)
|
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
GError* error = NULL;
|
2019-03-07 12:58:15 -05:00
|
|
|
|
|
|
|
if (!g_key_file_save_to_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, &error)) {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_error("[OMEMO] error saving identity to: %s, %s", omemo_ctx.identity_filename->str, error->message);
|
2019-03-07 12:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-07 08:18:57 -04:00
|
|
|
GKeyFile*
|
2019-04-08 21:01:02 -04:00
|
|
|
omemo_trust_keyfile(void)
|
|
|
|
{
|
|
|
|
return omemo_ctx.trust_keyfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
omemo_trust_keyfile_save(void)
|
|
|
|
{
|
2020-07-07 08:18:57 -04:00
|
|
|
GError* error = NULL;
|
2019-04-08 21:01:02 -04:00
|
|
|
|
|
|
|
if (!g_key_file_save_to_file(omemo_ctx.trust_keyfile, omemo_ctx.trust_filename->str, &error)) {
|
2021-03-06 13:51:34 -05:00
|
|
|
log_error("[OMEMO] error saving trust to: %s, %s", omemo_ctx.trust_filename->str, error->message);
|
|