From 91c5225e55dcf6fe403b61bc00eec28b04211b18 Mon Sep 17 00:00:00 2001 From: Gergely Polonkai Date: Fri, 4 Jan 2019 13:16:50 +0100 Subject: [PATCH] wip: start implementing muxrpc --- ssb-gtk/sbot.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++- ssb-gtk/sbot.h | 2 + 2 files changed, 178 insertions(+), 3 deletions(-) diff --git a/ssb-gtk/sbot.c b/ssb-gtk/sbot.c index d0f506c..6bc57bb 100644 --- a/ssb-gtk/sbot.c +++ b/ssb-gtk/sbot.c @@ -28,6 +28,22 @@ struct _SsbScuttler { GSocketConnection *connection; }; +typedef enum { + SSB_RPC_ASYNC, + SSB_RPC_SOURCE, + SSB_RPC_SINK, + SSB_RPC_DUPLEX +} SsbRPCType; + +typedef enum { + SSB_PACKET_TYPE_BUFFER = 0, + SSB_PACKET_TYPE_STRING = 1, + SSB_PACKET_TYPE_JSON = 2, +} SsbPacketType; + +#define MAX_MESSAGE_SIZE 32768 +#define BOXS_MAXLEN 4096 + G_DEFINE_QUARK(ssb_scttler_error_quark, ssb_scuttler_error); G_DEFINE_TYPE(SsbScuttler, ssb_scuttler, G_TYPE_INITIALLY_UNOWNED); @@ -37,6 +53,16 @@ typedef struct { guint column; } ParseData; +typedef struct { + guint32 len; + gint32 request_id; +} PacketHeader; + +typedef struct { + guint16 len; + guint8 mac[16]; +} BoxsHeader; + static const guchar zeros[24] = {0}; static const guchar ssb_cap[] = { @@ -288,7 +314,7 @@ second_hook_cb() } static gboolean -ssb_scuttler_send(SsbScuttler *scuttler, gpointer data, gsize data_len, GError **error) +ssb_scuttler_send_plain(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; @@ -362,7 +388,7 @@ ssb_scuttler_shs_connect(SsbScuttler *scuttler, GError **error) 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)) { + if (!ssb_scuttler_send_plain(scuttler, buf, sizeof(buf), &err)) { g_propagate_error(error, err); return FALSE; @@ -438,7 +464,7 @@ ssb_scuttler_shs_connect(SsbScuttler *scuttler, GError **error) return FALSE; } - if (!ssb_scuttler_send(scuttler, boxed_auth, sizeof(boxed_auth), &err)) { + if (!ssb_scuttler_send_plain(scuttler, boxed_auth, sizeof(boxed_auth), &err)) { g_propagate_error(error, err); return FALSE; @@ -563,6 +589,153 @@ ssb_scuttler_connect(SsbScuttler *scuttler, GError **error) return TRUE; } +static void +increment_nonce(guchar nonce[24]) +{ + int i; + + for (i = 23; (i >= 0) && (nonce[i] == 0xff); i--) { + nonce[i] = 0; + } + + if (i >= 0) { + nonce[i]++; + } +} + +static gboolean +ssb_scuttler_write_packet(SsbScuttler *scuttler, const guchar *data, guint16 data_len, GError **error) +{ + gsize boxed_len = data_len + 34; + guchar boxed[boxed_len]; + GError *err = NULL; + + increment_nonce(scuttler->nonce2); + + if (crypto_secretbox_easy(boxed + 18, data, data_len, scuttler->nonce2, scuttler->encrypt_key) < 0) { + g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_SEND, "Failed to box packet data"); + + return FALSE; + } + + BoxsHeader header; + + header.len = g_htons(data_len); + memcpy(header.mac, boxed + 18, 16); + + if (crypto_secretbox_easy(boxed, (guchar *)&header, 18, scuttler->nonce1, scuttler->encrypt_key) < 0) { + g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_SEND, "Failed to box packet header"); + + return FALSE; + } + + increment_nonce(scuttler->nonce1); + increment_nonce(scuttler->nonce1); + increment_nonce(scuttler->nonce2); + + if (!ssb_scuttler_send_plain(scuttler, boxed, 34, &err)) { + g_propagate_error(error, err); + + return FALSE; + } + + return TRUE; +} + +static gboolean +ssb_scuttler_send_bytes(SsbScuttler *scuttler, const guchar *data, gsize data_len, GError **error) +{ + GError *err = NULL; + + while(data_len > 0) { + gsize len = data_len > BOXS_MAXLEN ? BOXS_MAXLEN : data_len; + + if (!ssb_scuttler_write_packet(scuttler, data, len, &err)) { + g_propagate_error(error, err); + + return FALSE; + } + + data_len -= len; + data += len; + } + + return TRUE; +} + +static gboolean +ssb_scuttler_send_packet(SsbScuttler *scuttler, + SsbPacketType type, + const gchar *data, + gsize data_len, + int request_id, + gboolean stream, + gboolean end, + GError **error) +{ + gsize out_len = data_len + 9; + guchar out_buf[out_len]; + PacketHeader header = { + g_htonl(data_len), + g_htonl(request_id) + }; + GError *err = NULL; + + out_buf[0] = (stream << 3) | (end << 2) | (type & 3); + memcpy(out_buf + 1, &header, 8); + memcpy(out_buf + 9, data, data_len); + + if (!ssb_scuttler_send_bytes(scuttler, out_buf, out_len, &err)) { + g_propagate_error(error, err); + + return FALSE; + } + + return TRUE; +} + +static void +ssb_scuttler_muxrpc_call(SsbScuttler *scuttler, + const gchar *method, + const gchar *argument, + SsbRPCType type, + const gchar *typestr, + int req_id, + GError **error) +{ + gchar *request; + gsize request_len; + gboolean is_request = (type == SSB_RPC_ASYNC); + JsonObject *req_object = json_object_new(); + JsonNode *req_node = json_node_new(JSON_NODE_OBJECT); + GError *err = NULL; + + json_object_set_string_member(req_object, "name", method); + json_object_set_string_member(req_object, "args", argument); + + if (!is_request) { + json_object_set_string_member(req_object, "type", typestr); + } + + json_node_take_object(req_node, req_object); + request = json_to_string(req_node, FALSE); + json_node_free(req_node); + + if ((request_len = strlen(request)) > MAX_MESSAGE_SIZE) { + g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_REQUEST_TOO_LARGE, "Request too large"); + + return; + }zq + + if (!ssb_scuttler_send_packet(scuttler, request, request_len, SSB_PACKET_TYPE_JSON, req_id, !is_request, FALSE, &err)) { + g_propagate_error(error, err); + + return; + } + + g_free(request); +} + static void ssb_scuttler_dispose(GObject *gobject) { diff --git a/ssb-gtk/sbot.h b/ssb-gtk/sbot.h index 207efc0..b333199 100644 --- a/ssb-gtk/sbot.h +++ b/ssb-gtk/sbot.h @@ -11,6 +11,8 @@ typedef enum { SSB_SCUTTLER_ERROR_KEYAUTH, SSB_SCUTTLER_ERROR_KEYVERIFY, SSB_SCUTTLER_ERROR_AUTH, + SSB_SCUTTLER_ERROR_REQUEST_TOO_LARGE, + SSB_SCUTTLER_ERROR_SEND, } SsbScuttlerError; G_BEGIN_DECLS