|
@@ -0,0 +1,206 @@
|
|
|
|
|
+/* iridium-remote.c
|
|
|
|
|
+ *
|
|
|
|
|
+ * Copyright 2018 Matthias Vogelgesang
|
|
|
|
|
+ *
|
|
|
|
|
+ * This program 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.
|
|
|
|
|
+ *
|
|
|
|
|
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+#include <json-glib/json-glib.h>
|
|
|
|
|
+#include <libsoup/soup.h>
|
|
|
|
|
+#include "iridium-remote.h"
|
|
|
|
|
+
|
|
|
|
|
+struct _IridiumRemote {
|
|
|
|
|
+ GObject parent_instance;
|
|
|
|
|
+
|
|
|
|
|
+ SoupSession *session;
|
|
|
|
|
+ SoupURI *base_uri;
|
|
|
|
|
+ gchar *password;
|
|
|
|
|
+ IridiumAuthParams params;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+G_DEFINE_TYPE (IridiumRemote, iridium_remote, G_TYPE_OBJECT)
|
|
|
|
|
+
|
|
|
|
|
+IridiumRemote *
|
|
|
|
|
+iridium_remote_new (void)
|
|
|
|
|
+{
|
|
|
|
|
+ return g_object_new (IRIDIUM_TYPE_REMOTE, NULL);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void
|
|
|
|
|
+on_auth_params_response_parsed (GObject *object,
|
|
|
|
|
+ GAsyncResult *result,
|
|
|
|
|
+ gpointer user_data)
|
|
|
|
|
+{
|
|
|
|
|
+ GTask *task;
|
|
|
|
|
+ IridiumRemote *self;
|
|
|
|
|
+ JsonParser *parser;
|
|
|
|
|
+ JsonObject *root;
|
|
|
|
|
+ const gchar *version;
|
|
|
|
|
+ GError *error = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ task = user_data;
|
|
|
|
|
+ self = g_task_get_task_data (task);
|
|
|
|
|
+ parser = JSON_PARSER (object);
|
|
|
|
|
+
|
|
|
|
|
+ if (!json_parser_load_from_stream_finish (parser, result, &error)) {
|
|
|
|
|
+ g_task_return_error (task, error);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ root = json_node_get_object (json_parser_get_root (parser));
|
|
|
|
|
+
|
|
|
|
|
+ self->params.func = IRIDIUM_CRYPTO_SF_FUNC_PBKDF2;
|
|
|
|
|
+ self->params.hash = IRIDIUM_CRYPTO_SF_HASH_SHA512;
|
|
|
|
|
+ self->params.cost = json_object_get_int_member (root, "pw_cost");
|
|
|
|
|
+ self->params.key_size = json_object_get_int_member (root, "pw_key_size");
|
|
|
|
|
+ self->params.salt = g_strdup (json_object_get_string_member (root, "pw_salt"));
|
|
|
|
|
+
|
|
|
|
|
+ if (g_strcmp0 (json_object_get_string_member (root, "pw_alg"), "sha512")) {
|
|
|
|
|
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
+ "Hash algorithm other than sha512 is not supported");
|
|
|
|
|
+ g_task_return_error (task, error);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (g_strcmp0 (json_object_get_string_member (root, "pw_func"), "pbkdf2")) {
|
|
|
|
|
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
+ "Password derivative function other than PBKDF2 is not supported");
|
|
|
|
|
+ g_task_return_error (task, error);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ version = json_object_get_string_member (root, "version");
|
|
|
|
|
+
|
|
|
|
|
+ if (!g_strcmp0 (version, "001"))
|
|
|
|
|
+ self->params.version = IRIDIUM_CRYPTO_SF_VERSION_001;
|
|
|
|
|
+ else if (!g_strcmp0 (version, "002"))
|
|
|
|
|
+ self->params.version = IRIDIUM_CRYPTO_SF_VERSION_002;
|
|
|
|
|
+ else {
|
|
|
|
|
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
+ "StandardFile protocols other than 001 and 002 are not supported");
|
|
|
|
|
+ g_task_return_error (task, error);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iridium_crypto_derive_keys (&self->params, self->password);
|
|
|
|
|
+
|
|
|
|
|
+ g_debug ("StandardFile parameters: version=%i func=%i hash=%i key_size=%u iterations=%u",
|
|
|
|
|
+ self->params.version,
|
|
|
|
|
+ self->params.func,
|
|
|
|
|
+ self->params.hash,
|
|
|
|
|
+ self->params.key_size,
|
|
|
|
|
+ self->params.cost);
|
|
|
|
|
+
|
|
|
|
|
+ g_object_unref (parser);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void
|
|
|
|
|
+on_send_auth_params_message (GObject *object,
|
|
|
|
|
+ GAsyncResult *result,
|
|
|
|
|
+ gpointer user_data)
|
|
|
|
|
+{
|
|
|
|
|
+ GInputStream *stream;
|
|
|
|
|
+ GTask *task;
|
|
|
|
|
+ JsonParser *parser;
|
|
|
|
|
+ GError *error = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ task = user_data;
|
|
|
|
|
+ stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error);
|
|
|
|
|
+
|
|
|
|
|
+ if (stream == NULL) {
|
|
|
|
|
+ g_task_return_error (task, error);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ parser = json_parser_new ();
|
|
|
|
|
+ json_parser_load_from_stream_async (parser, stream, g_task_get_cancellable (task),
|
|
|
|
|
+ on_auth_params_response_parsed, task);
|
|
|
|
|
+ /* g_object_unref (object); */
|
|
|
|
|
+ g_object_unref (stream);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void
|
|
|
|
|
+iridium_remote_get_auth_params_async (IridiumRemote *remote,
|
|
|
|
|
+ const gchar *server,
|
|
|
|
|
+ const gchar *email,
|
|
|
|
|
+ const gchar *password,
|
|
|
|
|
+ GCancellable *cancellable,
|
|
|
|
|
+ GAsyncReadyCallback callback,
|
|
|
|
|
+ gpointer user_data)
|
|
|
|
|
+{
|
|
|
|
|
+ SoupURI *uri;
|
|
|
|
|
+ SoupRequestHTTP *request;
|
|
|
|
|
+ GTask *task;
|
|
|
|
|
+ GError *error = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ if (remote->base_uri)
|
|
|
|
|
+ soup_uri_free (remote->base_uri);
|
|
|
|
|
+
|
|
|
|
|
+ remote->base_uri = soup_uri_new (server);
|
|
|
|
|
+ remote->password = g_strdup (password);
|
|
|
|
|
+
|
|
|
|
|
+ uri = soup_uri_new_with_base (remote->base_uri, "api/auth/params");
|
|
|
|
|
+ soup_uri_set_query_from_fields (uri, "email", email, NULL);
|
|
|
|
|
+ task = g_task_new (remote, cancellable, callback, user_data);
|
|
|
|
|
+ /* TODO: what happens to request? */
|
|
|
|
|
+ request = soup_session_request_http_uri (remote->session, "GET", uri, &error);
|
|
|
|
|
+
|
|
|
|
|
+ if (request == NULL) {
|
|
|
|
|
+ g_task_return_error (task, error);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ g_task_set_task_data (task, remote, NULL);
|
|
|
|
|
+ soup_request_send_async (SOUP_REQUEST (request), cancellable, on_send_auth_params_message, task);
|
|
|
|
|
+ soup_uri_free (uri);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const IridiumAuthParams *
|
|
|
|
|
+iridium_remote_get_auth_params_finish (IridiumRemote *remote,
|
|
|
|
|
+ GAsyncResult *result,
|
|
|
|
|
+ GError **error)
|
|
|
|
|
+{
|
|
|
|
|
+ g_return_val_if_fail (g_task_is_valid (result, remote), NULL);
|
|
|
|
|
+ return &remote->params;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void
|
|
|
|
|
+iridium_remote_dispose (GObject *object)
|
|
|
|
|
+{
|
|
|
|
|
+ IridiumRemote *self;
|
|
|
|
|
+
|
|
|
|
|
+ self = IRIDIUM_REMOTE (object);
|
|
|
|
|
+ g_object_unref (self->session);
|
|
|
|
|
+
|
|
|
|
|
+ if (self->base_uri)
|
|
|
|
|
+ soup_uri_free (self->base_uri);
|
|
|
|
|
+
|
|
|
|
|
+ G_OBJECT_CLASS (iridium_remote_parent_class)->dispose (object);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void
|
|
|
|
|
+iridium_remote_class_init (IridiumRemoteClass *klass)
|
|
|
|
|
+{
|
|
|
|
|
+ GObjectClass *oclass;
|
|
|
|
|
+
|
|
|
|
|
+ oclass = G_OBJECT_CLASS (klass);
|
|
|
|
|
+ oclass->dispose = iridium_remote_dispose;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void
|
|
|
|
|
+iridium_remote_init (IridiumRemote *self)
|
|
|
|
|
+{
|
|
|
|
|
+ self->base_uri = NULL;
|
|
|
|
|
+ self->session = soup_session_new ();
|
|
|
|
|
+}
|