wip: scuttler is a gobject, authentication
This commit is contained in:
parent
661867e0ba
commit
c59f8ab201
@ -5,9 +5,13 @@ i18n = import('i18n')
|
||||
|
||||
glib_required = '>= 2.40'
|
||||
gtk_required = '>= 3.20'
|
||||
json_glib_required = '>= 1.4.0'
|
||||
libsodium_required = '>= 1.0.0'
|
||||
|
||||
glib = dependency('glib-2.0', version: glib_required)
|
||||
gtk = dependency('gtk+-3.0', version: gtk_required)
|
||||
json_glib = dependency('json-glib-1.0', version: json_glib_required)
|
||||
libsodium = dependency('libsodium', version: libsodium_required)
|
||||
|
||||
subdir('data')
|
||||
subdir('ssb-gtk')
|
||||
|
@ -6,5 +6,5 @@ sources = [
|
||||
]
|
||||
|
||||
executable('ssb-gtk', sources, ssb_resources,
|
||||
dependencies: [glib, gtk],
|
||||
dependencies: [glib, gtk, json_glib, libsodium],
|
||||
install: true)
|
||||
|
680
ssb-gtk/sbot.c
680
ssb-gtk/sbot.c
@ -1,22 +1,678 @@
|
||||
#include <glib.h>
|
||||
#include <json-glib/json-glib.h>
|
||||
#include <sodium.h>
|
||||
|
||||
gboolean do_scuttling = TRUE;
|
||||
#include "sbot.h"
|
||||
|
||||
struct _SsbScuttler {
|
||||
GObject parent_instance;
|
||||
|
||||
// If TRUE, the scuttler is already initialised
|
||||
gboolean initialised;
|
||||
|
||||
// The SSB directory
|
||||
gchar *ssb_dir;
|
||||
|
||||
guchar *app_key;
|
||||
guchar *private_key;
|
||||
guchar *encrypt_key;
|
||||
guchar *decrypt_key;
|
||||
guchar nonce1[24];
|
||||
guchar nonce2[24];
|
||||
guchar rx_nonce[24];
|
||||
gsize rx_buf_pos;
|
||||
gsize rx_buf_len;
|
||||
gboolean noauth;
|
||||
gboolean wrote_goodbye;
|
||||
GSocketClient *socket_client;
|
||||
GSocketConnection *connection;
|
||||
};
|
||||
|
||||
G_DEFINE_QUARK(ssb_scttler_error_quark, ssb_scuttler_error);
|
||||
G_DEFINE_TYPE(SsbScuttler, ssb_scuttler, G_TYPE_INITIALLY_UNOWNED);
|
||||
|
||||
typedef struct {
|
||||
gboolean valid;
|
||||
guint line;
|
||||
guint column;
|
||||
} ParseData;
|
||||
|
||||
static const guchar zeros[24] = {0};
|
||||
|
||||
static const guchar ssb_cap[] = {
|
||||
0xd4, 0xa1, 0xcb, 0x88, 0xa6, 0x6f, 0x02, 0xf8,
|
||||
0xdb, 0x63, 0x5c, 0xe2, 0x64, 0x41, 0xcc, 0x5d,
|
||||
0xac, 0x1b, 0x08, 0x42, 0x0c, 0xea, 0xac, 0x23,
|
||||
0x08, 0x39, 0xb7, 0x55, 0x84, 0x5a, 0x9f, 0xfb
|
||||
};
|
||||
|
||||
GMainLoop *scuttle_loop = NULL;
|
||||
SsbScuttler *singleton = NULL;
|
||||
|
||||
static gchar *
|
||||
read_config(const gchar *base_dir, const gchar *file_name, GError **error) {
|
||||
gchar *config_file_name;
|
||||
GFile *config_file;
|
||||
GFileInfo *config_info;
|
||||
goffset config_size;
|
||||
GFileInputStream *config_stream;
|
||||
GError *err = NULL;
|
||||
gchar *config_data = NULL;
|
||||
|
||||
config_file_name = g_strdup_printf("%s/%s", base_dir, file_name);
|
||||
config_file = g_file_new_for_path(config_file_name);
|
||||
g_free(config_file_name);
|
||||
|
||||
if ((config_info = g_file_query_info(config_file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, &err)) == NULL) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
goto ret_err1;
|
||||
}
|
||||
|
||||
config_size = g_file_info_get_size(config_info);
|
||||
|
||||
config_data = g_new0(gchar, config_size);
|
||||
|
||||
if ((config_stream = g_file_read(config_file, NULL, &err)) == NULL) {
|
||||
g_propagate_error(error, err);
|
||||
g_clear_pointer(&config_data, g_free);
|
||||
|
||||
goto ret_err2;
|
||||
}
|
||||
|
||||
if (!g_input_stream_read_all(G_INPUT_STREAM(config_stream), config_data, config_size, NULL, NULL, &err)) {
|
||||
g_propagate_error(error, err);
|
||||
g_clear_pointer(&config_data, g_free);
|
||||
|
||||
goto ret_err3;
|
||||
}
|
||||
|
||||
if (!g_input_stream_close(G_INPUT_STREAM(config_stream), NULL, &err)) {
|
||||
g_propagate_error(error, err);
|
||||
g_clear_pointer(&config_data, g_free);
|
||||
|
||||
goto ret_err3;
|
||||
}
|
||||
|
||||
ret_err3:
|
||||
g_object_unref(config_info);
|
||||
|
||||
ret_err2:
|
||||
g_object_unref(config_stream);
|
||||
|
||||
ret_err1:
|
||||
g_object_unref(config_file);
|
||||
|
||||
return config_data;
|
||||
}
|
||||
|
||||
static void
|
||||
json_error(JsonParser *parser, GError *error, ParseData *parse_data)
|
||||
{
|
||||
if ((error->domain != JSON_PARSER_ERROR) || (error->code != JSON_PARSER_ERROR_INVALID_BAREWORD)) {
|
||||
parse_data->valid = FALSE;
|
||||
} else {
|
||||
parse_data->valid = TRUE;
|
||||
parse_data->line = json_parser_get_current_line(parser) - 1;
|
||||
parse_data->column = json_parser_get_current_pos(parser) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static JsonNode *
|
||||
parse_commented_json(gchar *json_data, GError **error)
|
||||
{
|
||||
ParseData parse_data;
|
||||
JsonParser *parser;
|
||||
GError *err = NULL;
|
||||
JsonNode *result = NULL;
|
||||
|
||||
parse_data.valid = FALSE;
|
||||
parser = json_parser_new();
|
||||
g_signal_connect(parser, "error", G_CALLBACK(json_error), &parse_data);
|
||||
|
||||
while (!json_parser_load_from_data(parser, json_data, -1, &err)) {
|
||||
gchar **config_lines;
|
||||
|
||||
if (parse_data.valid != TRUE) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// XXX: Will this work under OS/X and Windows?
|
||||
config_lines = g_strsplit(json_data, "\n", 0);
|
||||
|
||||
if (config_lines[parse_data.line][parse_data.column] == '#') {
|
||||
guint i;
|
||||
guint start_pos = 0;
|
||||
guint end_pos;
|
||||
|
||||
for (i = 0; i <= parse_data.line; i++) {
|
||||
if (i < parse_data.line) {
|
||||
start_pos += strlen(config_lines[i]) + 1;
|
||||
}
|
||||
|
||||
end_pos = start_pos;
|
||||
end_pos += strlen(config_lines[i]);
|
||||
}
|
||||
|
||||
start_pos += parse_data.column;
|
||||
|
||||
for (i = start_pos; i < end_pos; i++) {
|
||||
json_data[i] = ' ';
|
||||
}
|
||||
|
||||
g_clear_error(&err);
|
||||
parse_data.valid = FALSE;
|
||||
}
|
||||
|
||||
g_strfreev(config_lines);
|
||||
}
|
||||
|
||||
result = json_parser_steal_root(parser);
|
||||
|
||||
g_object_unref(parser);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
initialise(SsbScuttler *scuttler, gchar *ssb_dir)
|
||||
{
|
||||
gchar *config_data = NULL;
|
||||
GError *err = NULL;
|
||||
JsonNode *json_content = NULL;
|
||||
|
||||
g_return_if_fail(scuttler != NULL);
|
||||
|
||||
if (scuttler->initialised == TRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((config_data = read_config(ssb_dir, "config", &err)) != NULL) {
|
||||
if ((json_content = parse_commented_json(config_data, &err)) == NULL) {
|
||||
g_critical("Could not parse configuration data");
|
||||
|
||||
g_free(config_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(config_data);
|
||||
g_clear_pointer(&json_content, json_node_free);
|
||||
}
|
||||
|
||||
if ((config_data = read_config(ssb_dir, "secret", &err)) != NULL) {
|
||||
JsonObject *secret_object;
|
||||
gchar *private_key = NULL;
|
||||
guchar *decoded_private_key = NULL;
|
||||
size_t private_key_len;
|
||||
gsize key_len;
|
||||
|
||||
if ((json_content = parse_commented_json(config_data, &err)) == NULL) {
|
||||
g_critical("Could not parse secret data");
|
||||
|
||||
g_free(config_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
secret_object = json_node_get_object(json_content);
|
||||
|
||||
if ((private_key = g_strdup(json_object_get_string_member(secret_object, "private"))) == NULL) {
|
||||
g_critical("Could not read private key, can not continue");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private_key_len = strlen(private_key);
|
||||
|
||||
if ((strlen(private_key) > 8) &&
|
||||
(strcmp(private_key + private_key_len - 8, ".ed25519") == 0)) {
|
||||
private_key[private_key_len - 8] = 0;
|
||||
}
|
||||
|
||||
if ((decoded_private_key = g_base64_decode(private_key, &key_len)) == NULL) {
|
||||
g_critical("Could not decode private key, can not continue");
|
||||
g_free(private_key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(private_key);
|
||||
g_free(config_data);
|
||||
g_clear_pointer(&json_content, json_node_free);
|
||||
|
||||
g_clear_pointer(&(scuttler->private_key), g_free);
|
||||
scuttler->private_key = decoded_private_key;
|
||||
}
|
||||
|
||||
scuttler->app_key = g_new0(guchar, sizeof(ssb_cap));
|
||||
memcpy(scuttler->app_key, ssb_cap, sizeof(ssb_cap));
|
||||
|
||||
scuttler->initialised = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ensure_scuttler():
|
||||
* @ssb_dir: (nullable): the SSB directory
|
||||
*
|
||||
* Ensure the scuttler singleton is initialised with @ssb_dir as its base directory.
|
||||
*/
|
||||
static inline gboolean
|
||||
ensure_scuttler(gchar *ssb_dir)
|
||||
{
|
||||
if (singleton != NULL) {
|
||||
if ((ssb_dir != NULL) && g_strcmp0(ssb_dir, singleton->ssb_dir)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
singleton = g_object_new(SSB_TYPE_SCUTTLER, NULL);
|
||||
|
||||
if (ssb_dir != NULL) {
|
||||
initialise(singleton, ssb_dir);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
second_hook_cb()
|
||||
{
|
||||
g_print("Scuttling…\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ssb_scuttler_send(SsbScuttler *scuttler, gpointer data, gsize data_len, GError **error)
|
||||
{
|
||||
GOutputStream *stream = g_io_stream_get_output_stream(G_IO_STREAM(scuttler->connection));
|
||||
GError *err = NULL;
|
||||
|
||||
if (!g_output_stream_write_all(stream, data, data_len, NULL, NULL, &err)) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ssb_scuttler_read(SsbScuttler *scuttler, gpointer buffer, gsize buffer_len, GError **error)
|
||||
{
|
||||
GInputStream *stream = g_io_stream_get_input_stream(G_IO_STREAM(scuttler->connection));
|
||||
GError *err = NULL;
|
||||
|
||||
if (g_input_stream_read(stream, buffer, buffer_len, NULL, &err) < 0) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ssb_scuttler_shs_connect(SsbScuttler *scuttler, GError **error)
|
||||
{
|
||||
guchar kx_pk[crypto_box_PUBLICKEYBYTES];
|
||||
guchar kx_sk[crypto_box_SECRETKEYBYTES];
|
||||
guchar local_app_mac[crypto_box_PUBLICKEYBYTES];
|
||||
guchar remote_app_mac[crypto_box_PUBLICKEYBYTES];
|
||||
guchar remote_kx_pk[crypto_box_PUBLICKEYBYTES];
|
||||
guchar buf[2 * crypto_box_PUBLICKEYBYTES];
|
||||
guchar secret[crypto_box_SECRETKEYBYTES];
|
||||
guchar remote_pk_curve[crypto_box_PUBLICKEYBYTES];
|
||||
guchar a_bob[crypto_box_PUBLICKEYBYTES];
|
||||
guchar secret2a[crypto_box_PUBLICKEYBYTES * 3];
|
||||
guchar secret2[crypto_box_PUBLICKEYBYTES];
|
||||
guchar shash[crypto_box_PUBLICKEYBYTES];
|
||||
guchar signed1[3 * crypto_box_PUBLICKEYBYTES];
|
||||
guchar sig[2 * crypto_box_PUBLICKEYBYTES];
|
||||
guchar hello[3 * crypto_box_PUBLICKEYBYTES];
|
||||
guchar boxed_auth[112];
|
||||
guchar boxed_response[80];
|
||||
guchar local_sk_curve[crypto_box_SECRETKEYBYTES];
|
||||
guchar b_alice[crypto_box_SECRETKEYBYTES];
|
||||
guchar secret3a[4 * crypto_box_SECRETKEYBYTES];
|
||||
guchar secret3[crypto_box_SECRETKEYBYTES];
|
||||
guchar signed2[160];
|
||||
guchar enc_key_hashed[crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES];
|
||||
guchar dec_key_hashed[crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES];
|
||||
GError *err = NULL;
|
||||
|
||||
if (crypto_box_keypair(kx_pk, kx_sk) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_KEYGEN, "Could not generate auth keypair");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_auth(local_app_mac, kx_pk, crypto_box_PUBLICKEYBYTES, scuttler->app_key) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_KEYAUTH, "Failed to generate app mac");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Send challenge
|
||||
memcpy(buf, local_app_mac, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(buf + crypto_box_PUBLICKEYBYTES, kx_pk, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
if (!ssb_scuttler_send(scuttler, buf, sizeof(buf), &err)) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Receive challenge response
|
||||
if (!ssb_scuttler_read(scuttler, buf, sizeof(buf), &err)) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(remote_app_mac, buf, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(remote_kx_pk, buf + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
if (crypto_auth_verify(buf, remote_kx_pk, crypto_box_PUBLICKEYBYTES, scuttler->app_key) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_KEYVERIFY, "Wrong protocol (version?)");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Send auth
|
||||
if (crypto_scalarmult(secret, kx_sk, remote_kx_pk) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to derive shared secret");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_sign_ed25519_pk_to_curve25519(remote_pk_curve, scuttler->private_key + crypto_box_SECRETKEYBYTES) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to curvify remote public key");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_scalarmult(a_bob, kx_sk, remote_pk_curve) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to derive a_bob");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(secret2a, scuttler->app_key, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(secret2a + crypto_box_PUBLICKEYBYTES, secret, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(secret2a + 2 * crypto_box_PUBLICKEYBYTES, a_bob, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
if (crypto_hash_sha256(secret2, secret2a, sizeof(secret2a)) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to hash secret2");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_hash_sha256(shash, secret, sizeof(secret)) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to hash secret");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(signed1, scuttler->app_key, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(signed1 + crypto_box_PUBLICKEYBYTES, scuttler->private_key + crypto_box_SECRETKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(signed1 + 2 * crypto_box_PUBLICKEYBYTES, shash, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
if (crypto_sign_detached(sig, NULL, signed1, sizeof(signed1), scuttler->private_key) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to sign inner hello");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(hello, sig, 2 * crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(hello + 2 * crypto_box_PUBLICKEYBYTES, scuttler->private_key + crypto_box_SECRETKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
if (crypto_secretbox_easy(boxed_auth, hello, sizeof(hello), zeros, secret2) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to box hello");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ssb_scuttler_send(scuttler, boxed_auth, sizeof(boxed_auth), &err)) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Verify the auth response
|
||||
|
||||
if (!ssb_scuttler_read(scuttler, boxed_response, sizeof(boxed_response), &err)) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_sign_ed25519_sk_to_curve25519(local_sk_curve, scuttler->private_key) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to curvify local secret key");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_scalarmult(b_alice, local_sk_curve, remote_kx_pk) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to derive b_alice");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(secret3a, scuttler->app_key, 32);
|
||||
memcpy(secret3a + crypto_box_SECRETKEYBYTES, secret, crypto_box_SECRETKEYBYTES);
|
||||
memcpy(secret3a + 2 * crypto_box_SECRETKEYBYTES, a_bob, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(secret3a + 2 * crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES, b_alice, crypto_box_SECRETKEYBYTES);
|
||||
|
||||
if (crypto_hash_sha256(secret3, secret3a, sizeof(secret3a)) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to hash secret3");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_secretbox_open_easy(sig, boxed_response, sizeof(boxed_response), zeros, secret3) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to unbox the okay");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(signed2, scuttler->app_key, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(signed2 + crypto_box_PUBLICKEYBYTES, hello, 96);
|
||||
memcpy(signed2 + 128, shash, 32);
|
||||
|
||||
if (crypto_sign_verify_detached(sig, signed2, sizeof(signed2), scuttler->private_key + crypto_box_SECRETKEYBYTES) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Server not authenticated");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (crypto_hash_sha256(secret, secret3, crypto_box_PUBLICKEYBYTES) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to hash secret3");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(enc_key_hashed, secret, crypto_box_SECRETKEYBYTES);
|
||||
memcpy(enc_key_hashed + crypto_box_SECRETKEYBYTES, scuttler->private_key + crypto_box_SECRETKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
g_clear_pointer(&(scuttler->encrypt_key), g_free);
|
||||
scuttler->encrypt_key = g_new0(guchar, crypto_box_SECRETKEYBYTES);
|
||||
|
||||
if (crypto_hash_sha256(scuttler->encrypt_key, enc_key_hashed, 64) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to hash the encrypt key");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_clear_pointer(&(scuttler->decrypt_key), g_free);
|
||||
scuttler->decrypt_key = g_new0(guchar, crypto_box_SECRETKEYBYTES);
|
||||
|
||||
memcpy(dec_key_hashed, secret, crypto_box_SECRETKEYBYTES);
|
||||
memcpy(dec_key_hashed + crypto_box_SECRETKEYBYTES, scuttler->private_key + crypto_box_SECRETKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
if (crypto_hash_sha256(scuttler->decrypt_key, dec_key_hashed, 64) < 0) {
|
||||
g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_AUTH, "Failed to hash the decrypt_key");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(scuttler->nonce1, remote_app_mac, 24);
|
||||
memcpy(scuttler->nonce2, remote_app_mac, 24);
|
||||
memcpy(scuttler->rx_nonce, local_app_mac, 24);
|
||||
scuttler->rx_buf_pos = 0;
|
||||
scuttler->rx_buf_len = 0;
|
||||
scuttler->noauth = FALSE;
|
||||
scuttler->wrote_goodbye = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ssb_scuttler_connect(SsbScuttler *scuttler, GError **error)
|
||||
{
|
||||
GInetAddress *address;
|
||||
GSocketAddress *destination;
|
||||
GError *err = NULL;
|
||||
|
||||
scuttler->socket_client = g_socket_client_new();
|
||||
|
||||
// TODO: Implement the UNIX socket version, too! Also, read this stuff from the
|
||||
// config/command line!
|
||||
address = g_inet_address_new_from_string("127.0.0.1");
|
||||
destination = g_inet_socket_address_new(address, 8008);
|
||||
g_object_unref(address);
|
||||
|
||||
scuttler->connection = g_socket_client_connect(scuttler->socket_client,
|
||||
G_SOCKET_CONNECTABLE(destination),
|
||||
NULL,
|
||||
&err);
|
||||
g_object_unref(destination);
|
||||
|
||||
// TODO: This is only required if noauth is not set
|
||||
if (!ssb_scuttler_shs_connect(scuttler, &err)) {
|
||||
g_propagate_error(error, err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ssb_scuttler_dispose(GObject *gobject)
|
||||
{
|
||||
SsbScuttler *scuttler = SSB_SCUTTLER(gobject);
|
||||
GError *err = NULL;
|
||||
|
||||
if (scuttler->connection && !g_io_stream_is_closed(G_IO_STREAM(scuttler->connection))) {
|
||||
g_io_stream_close(G_IO_STREAM(scuttler->connection), NULL, &err);
|
||||
scuttler->connection = NULL;
|
||||
}
|
||||
|
||||
g_clear_pointer(&(scuttler->ssb_dir), g_free);
|
||||
g_clear_pointer(&(scuttler->app_key), g_free);
|
||||
g_clear_pointer(&(scuttler->private_key), g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
ssb_scuttler_finalize(GObject *gobject)
|
||||
{
|
||||
SsbScuttler *scuttler = SSB_SCUTTLER(gobject);
|
||||
|
||||
g_clear_pointer(&(scuttler->socket_client), g_object_unref);
|
||||
g_clear_pointer(&(scuttler->ssb_dir), g_free);
|
||||
g_clear_pointer(&(scuttler->encrypt_key), g_free);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
ssb_scuttler_class_init(SsbScuttlerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
gobject_class->dispose = ssb_scuttler_dispose;
|
||||
gobject_class->finalize = ssb_scuttler_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
ssb_scuttler_init(SsbScuttler *scuttler)
|
||||
{
|
||||
scuttler->initialised = FALSE;
|
||||
scuttler->ssb_dir = NULL;
|
||||
scuttler->socket_client = NULL;
|
||||
scuttler->connection = NULL;
|
||||
scuttler->app_key = NULL;
|
||||
scuttler->private_key = NULL;
|
||||
scuttler->encrypt_key = NULL;
|
||||
}
|
||||
|
||||
gpointer
|
||||
scuttle(gchar *ssb_dir)
|
||||
{
|
||||
gchar *config_file = g_strdup_printf("%s/config", ssb_dir);
|
||||
g_print("Read config file %s\n", config_file);
|
||||
g_free(config_file);
|
||||
GMainContext *scuttle_context;
|
||||
GSource *second_hook;
|
||||
GError *err = NULL;
|
||||
|
||||
g_print("Starting scuttle\n");
|
||||
|
||||
while (do_scuttling) {
|
||||
g_usleep(G_USEC_PER_SEC);
|
||||
g_print("Scuttle…\n");
|
||||
}
|
||||
|
||||
g_print("Scuttling stopped\n");
|
||||
if (sodium_init() < 0) {
|
||||
g_critical("Can not initialise sodium");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Since scuttler is a singleton global to this file, we don’t need the return value of this
|
||||
// function now.
|
||||
if (G_UNLIKELY(!ensure_scuttler(ssb_dir))) {
|
||||
g_critical("Can not reinitialise scuttler with a new SSB directory.");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_object_ref_sink(singleton);
|
||||
|
||||
scuttle_context = g_main_context_new();
|
||||
scuttle_loop = g_main_loop_new(scuttle_context, FALSE);
|
||||
|
||||
// TODO: This is for debugging purposes only, let’s remove it as soon as the scuttler works
|
||||
second_hook = g_timeout_source_new_seconds(1);
|
||||
g_source_set_callback(second_hook,
|
||||
G_SOURCE_FUNC(second_hook_cb),
|
||||
g_main_loop_ref(scuttle_loop),
|
||||
(GDestroyNotify)g_main_loop_unref);
|
||||
g_source_attach(second_hook, scuttle_context);
|
||||
|
||||
// Set scuttle_context as the default context for this thread
|
||||
g_main_context_push_thread_default(scuttle_context);
|
||||
|
||||
if (!ssb_scuttler_connect(singleton, &err))
|
||||
{
|
||||
g_critical("Could not connect: %s", err->message);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_debug("Starting scuttle");
|
||||
|
||||
g_main_loop_run(scuttle_loop);
|
||||
g_main_context_pop_thread_default(scuttle_context);
|
||||
g_main_loop_unref(scuttle_loop);
|
||||
g_object_unref(singleton);
|
||||
|
||||
g_debug("Scuttling stopped");
|
||||
|
||||
scuttle_loop = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
stop_scuttling(void)
|
||||
{
|
||||
if (scuttle_loop) {
|
||||
g_main_loop_quit(scuttle_loop);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,26 @@
|
||||
#ifndef __SBOT_H__
|
||||
# define __SBOT_H__
|
||||
|
||||
# include <glib.h>
|
||||
# include <glib-object.h>
|
||||
|
||||
extern gboolean do_scuttling;
|
||||
# define SSB_TYPE_SCUTTLER ssb_scuttler_get_type()
|
||||
G_DECLARE_FINAL_TYPE(SsbScuttler, ssb_scuttler, SSB, SCUTTLER, GInitiallyUnowned)
|
||||
|
||||
typedef enum {
|
||||
SSB_SCUTTLER_ERROR_KEYGEN,
|
||||
SSB_SCUTTLER_ERROR_KEYAUTH,
|
||||
SSB_SCUTTLER_ERROR_KEYVERIFY,
|
||||
SSB_SCUTTLER_ERROR_AUTH,
|
||||
} SsbScuttlerError;
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gpointer scuttle(gchar *ssb_dir);
|
||||
void stop_scuttling(void);
|
||||
|
||||
# define SSB_SCUTTLER_ERROR (ssb_scuttler_error_quark())
|
||||
GQuark ssb_scuttler_error_quark(void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SBOT_H__ */
|
||||
|
@ -115,7 +115,7 @@ ssb_app_shutdown(GApplication *gapp)
|
||||
{
|
||||
SsbApp *app = SSB_APP(gapp);
|
||||
|
||||
do_scuttling = FALSE;
|
||||
stop_scuttling();
|
||||
|
||||
(void)g_thread_join(app->scuttle_thread);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user