[epiphany/wip/sync: 9/19] sync-service: Get crypto keys at sign in and store them in the secret schema
- From: Gabriel Ivașcu <gabrielivascu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/wip/sync: 9/19] sync-service: Get crypto keys at sign in and store them in the secret schema
- Date: Wed, 12 Apr 2017 18:36:33 +0000 (UTC)
commit 37818d01ef3b1f3ac27e111c4e9a8b0338f283d2
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date: Wed Apr 12 13:40:20 2017 +0300
sync-service: Get crypto keys at sign in and store them in the secret schema
src/sync/ephy-sync-secret.c | 3 +
src/sync/ephy-sync-service.c | 369 +++++++++++++++++++-----------------------
src/sync/ephy-sync-service.h | 6 +-
3 files changed, 174 insertions(+), 204 deletions(-)
---
diff --git a/src/sync/ephy-sync-secret.c b/src/sync/ephy-sync-secret.c
index 9d3b6dc..affb253 100644
--- a/src/sync/ephy-sync-secret.c
+++ b/src/sync/ephy-sync-secret.c
@@ -223,6 +223,9 @@ ephy_sync_secret_store_tokens (EphySyncService *service)
json_object_set_string_member (object,
ephy_sync_service_token_name_from_type (TOKEN_KB),
ephy_sync_service_get_token (service, TOKEN_KB));
+ json_object_set_string_member (object,
+ ephy_sync_service_token_name_from_type (TOKEN_CRYPTOKEYS),
+ ephy_sync_service_get_token (service, TOKEN_CRYPTOKEYS));
json_node_set_object (node, object);
tokens = json_to_string (node, FALSE);
value = secret_value_new (tokens, -1, "text/plain");
diff --git a/src/sync/ephy-sync-service.c b/src/sync/ephy-sync-service.c
index 07ea217..33cd7e8 100644
--- a/src/sync/ephy-sync-service.c
+++ b/src/sync/ephy-sync-service.c
@@ -49,7 +49,7 @@ struct _EphySyncService {
char *uid;
char *sessionToken;
char *kB;
- GHashTable *key_bundles;
+ char *crypto_keys;
char *user_email;
double sync_time;
@@ -112,7 +112,8 @@ typedef struct {
} SyncAsyncData;
static void ephy_sync_service_send_next_storage_request (EphySyncService *self);
-static void ephy_sync_service_obtain_sync_key_bundles (EphySyncService *self);
+static void ephy_sync_service_stop_periodical_sync (EphySyncService *self);
+static void ephy_sync_service_sync_frequency_changed_cb (EphySyncService *self);
static StorageRequestAsyncData *
storage_request_async_data_new (const char *endpoint,
@@ -242,69 +243,48 @@ sync_async_data_free (SyncAsyncData *data)
g_slice_free (SyncAsyncData, data);
}
-static gboolean
-ephy_sync_service_storage_credentials_is_expired (EphySyncService *self)
+static SyncCryptoKeyBundle *
+ephy_sync_service_get_key_bundle (EphySyncService *self,
+ const char *collection)
{
- g_assert (EPHY_IS_SYNC_SERVICE (self));
-
- if (!self->storage_credentials_id || !self->storage_credentials_key)
- return TRUE;
+ SyncCryptoKeyBundle *bundle = NULL;
+ JsonNode *node;
+ JsonObject *json;
+ JsonObject *collections;
+ JsonArray *array;
+ GError *error = NULL;
- if (self->storage_credentials_expiry_time == 0)
- return TRUE;
+ g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), NULL);
+ g_return_val_if_fail (collection, NULL);
+ g_return_val_if_fail (self->crypto_keys, NULL);
- /* Consider a 60 seconds safety interval. */
- return self->storage_credentials_expiry_time < g_get_real_time () / 1000000 - 60;
-}
+ node = json_from_string (self->crypto_keys, &error);
+ g_assert (!error);
+ json = json_node_get_object (node);
+ collections = json_object_get_object_member (json, "collections");
+ array = json_object_has_member (collections, collection) ?
+ json_object_get_array_member (collections, collection) :
+ json_object_get_array_member (json, "default");
+ bundle = ephy_sync_crypto_key_bundle_from_array (array);
-static void
-ephy_sync_service_stop_periodical_sync (EphySyncService *self)
-{
- g_assert (EPHY_IS_SYNC_SERVICE (self));
+ json_node_unref (node);
- if (self->source_id != 0) {
- g_source_remove (self->source_id);
- self->source_id = 0;
- }
+ return bundle;
}
static gboolean
-ephy_sync_service_sync (gpointer user_data)
-{
- EphySyncService *self = EPHY_SYNC_SERVICE (user_data);
- GList *managers = NULL;
-
- managers = ephy_shell_get_synchronizable_managers (ephy_shell_get_default ());
- if (managers) {
- ephy_sync_service_obtain_sync_key_bundles (self);
- g_list_free (managers);
- } else {
- g_signal_emit (self, signals[SYNC_FINISHED], 0);
- }
-
- return G_SOURCE_CONTINUE;
-}
-
-static void
-ephy_sync_service_schedule_periodical_sync (EphySyncService *self)
+ephy_sync_service_storage_credentials_is_expired (EphySyncService *self)
{
g_assert (EPHY_IS_SYNC_SERVICE (self));
- self->source_id = g_timeout_add_seconds (g_settings_get_uint (EPHY_SETTINGS_SYNC,
- EPHY_PREFS_SYNC_FREQUENCY) * 60,
- ephy_sync_service_sync,
- self);
- LOG ("Scheduled new sync with frequency %u mins",
- g_settings_get_uint (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_FREQUENCY));
-}
+ if (!self->storage_credentials_id || !self->storage_credentials_key)
+ return TRUE;
-static void
-ephy_sync_service_sync_frequency_changed_cb (EphySyncService *self)
-{
- g_assert (EPHY_IS_SYNC_SERVICE (self));
+ if (self->storage_credentials_expiry_time == 0)
+ return TRUE;
- ephy_sync_service_stop_periodical_sync (self);
- ephy_sync_service_schedule_periodical_sync (self);
+ /* Consider a 60 seconds safety interval. */
+ return self->storage_credentials_expiry_time < g_get_real_time () / 1000000 - 60;
}
static void
@@ -831,7 +811,6 @@ ephy_sync_service_finalize (GObject *object)
ephy_sync_crypto_rsa_key_pair_free (self->keypair);
g_queue_free_full (self->storage_queue, (GDestroyNotify) storage_request_async_data_free);
- g_hash_table_destroy (self->key_bundles);
G_OBJECT_CLASS (ephy_sync_service_parent_class)->finalize (object);
}
@@ -908,8 +887,6 @@ ephy_sync_service_init (EphySyncService *self)
self->session = soup_session_new ();
self->storage_queue = g_queue_new ();
- self->key_bundles = g_hash_table_new_full (g_str_hash, g_str_equal,
- NULL, (GDestroyNotify)ephy_sync_crypto_key_bundle_free);
settings = ephy_embed_prefs_get_settings ();
user_agent = webkit_settings_get_user_agent (settings);
@@ -974,6 +951,8 @@ ephy_sync_service_get_token (EphySyncService *self,
return self->sessionToken;
case TOKEN_KB:
return self->kB;
+ case TOKEN_CRYPTOKEYS:
+ return self->crypto_keys;
default:
g_assert_not_reached ();
}
@@ -1000,6 +979,10 @@ ephy_sync_service_set_token (EphySyncService *self,
g_free (self->kB);
self->kB = g_strdup (value);
break;
+ case TOKEN_CRYPTOKEYS:
+ g_free (self->crypto_keys);
+ self->crypto_keys = g_strdup (value);
+ break;
default:
g_assert_not_reached ();
}
@@ -1015,6 +998,8 @@ ephy_sync_service_token_name_from_type (EphySyncTokenType type)
return "sessionToken";
case TOKEN_KB:
return "kB";
+ case TOKEN_CRYPTOKEYS:
+ return "cryptoKeys";
default:
g_assert_not_reached ();
}
@@ -1029,25 +1014,11 @@ ephy_sync_service_token_type_from_name (const char *name)
return TOKEN_SESSIONTOKEN;
if (!g_strcmp0 (name, "kB"))
return TOKEN_KB;
+ if (!g_strcmp0 (name, "cryptoKeys"))
+ return TOKEN_CRYPTOKEYS;
g_assert_not_reached ();
}
-SyncCryptoKeyBundle *
-ephy_sync_service_get_key_bundle (EphySyncService *self,
- const char *collection)
-{
- SyncCryptoKeyBundle *bundle;
-
- g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), NULL);
- g_return_val_if_fail (collection, NULL);
-
- bundle = g_hash_table_lookup (self->key_bundles, collection);
- if (!bundle)
- bundle = g_hash_table_lookup (self->key_bundles, "default");
-
- return bundle;
-}
-
void
ephy_sync_service_clear_storage_credentials (EphySyncService *self)
{
@@ -1068,6 +1039,7 @@ ephy_sync_service_clear_tokens (EphySyncService *self)
g_clear_pointer (&self->uid, g_free);
g_clear_pointer (&self->sessionToken, g_free);
g_clear_pointer (&self->kB, g_free);
+ g_clear_pointer (&self->crypto_keys, g_free);
}
static void
@@ -1143,6 +1115,92 @@ ephy_sync_service_report_sign_in_error (EphySyncService *self,
}
static void
+obtain_crypto_keys_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ EphySyncService *self;
+ SyncCryptoKeyBundle *bundle;
+ JsonObject *json;
+ JsonNode *node;
+ GError *error = NULL;
+ const char *payload;
+ char *crypto_keys;
+ guint8 *kB;
+ gboolean is_internal_error = TRUE;
+
+ self = ephy_shell_get_sync_service (ephy_shell_get_default ());
+
+ if (msg->status_code == 404) {
+ /* TODO: Generate and upload new sync key bundles. */
+ is_internal_error = FALSE;
+ goto out;
+ }
+
+ if (msg->status_code != 200) {
+ g_warning ("Failed to get crypto/keys record. Status code: %u, response: %s",
+ msg->status_code, msg->response_body->data);
+ goto out;
+ }
+
+ node = json_from_string (msg->response_body->data, &error);
+ if (error) {
+ g_warning ("Response is not a valid JSON: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ if (!JSON_NODE_HOLDS_OBJECT (node)) {
+ g_warning ("JSON root does not hold an object");
+ goto free_node;
+ }
+ json = json_node_get_object (node);
+ if (!json_object_has_member (json, "payload")) {
+ g_warning ("JSON object is missing 'payload' member");
+ goto free_node;
+ }
+
+ /* Derive the Sync Key bundle from kB. The bundle consists of two 32 bytes keys:
+ * the first one used as a symmetric encryption key (AES) and the second one
+ * used as a HMAC key. */
+ kB = ephy_sync_crypto_decode_hex (ephy_sync_service_get_token (self, TOKEN_KB));
+ bundle = ephy_sync_crypto_derive_key_bundle (kB, TOKEN_LENGTH);
+ payload = json_object_get_string_member (json, "payload");
+ crypto_keys = ephy_sync_crypto_decrypt_record (payload, bundle);
+ if (!crypto_keys) {
+ g_warning ("Failed to decrypt crypto keys record");
+ goto free_bundle;
+ }
+
+ /* Proceed to store tokens. */
+ ephy_sync_service_set_token (self, crypto_keys, TOKEN_CRYPTOKEYS);
+ ephy_sync_secret_store_tokens (self);
+ is_internal_error = FALSE;
+
+ g_free (crypto_keys);
+free_bundle:
+ ephy_sync_crypto_key_bundle_free (bundle);
+ g_free (kB);
+free_node:
+ json_node_unref (node);
+out:
+ if (is_internal_error)
+ ephy_sync_service_report_sign_in_error (self,
+ _("Failed to retrieve crypto keys."),
+ TRUE);
+ ephy_sync_service_send_next_storage_request (self);
+}
+
+static void
+ephy_sync_service_obtain_crypto_keys (EphySyncService *self)
+{
+ g_assert (EPHY_IS_SYNC_SERVICE (self));
+
+ ephy_sync_service_queue_storage_request (self, "storage/crypto/keys",
+ SOUP_METHOD_GET, NULL, -1, -1,
+ obtain_crypto_keys_cb, NULL);
+}
+
+static void
check_storage_version_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
@@ -1210,8 +1268,8 @@ check_storage_version_cb (SoupSession *session,
goto free_payload;
}
- /* Proceed to store tokens. */
- ephy_sync_secret_store_tokens (self);
+ /* Proceed to obtain the crypto keys. */
+ ephy_sync_service_obtain_crypto_keys (self);
is_internal_error = FALSE;
free_payload:
@@ -1500,7 +1558,7 @@ download_synchronizable_cb (SoupSession *session,
synchronizable = EPHY_SYNCHRONIZABLE (ephy_synchronizable_from_bso (bso, type, bundle, &is_deleted));
if (!synchronizable) {
g_warning ("Failed to create synchronizable object from BSO");
- goto free_node;
+ goto free_bundle;
}
/* Delete the local object and add the remote one if it is not marked as deleted. */
@@ -1513,6 +1571,8 @@ download_synchronizable_cb (SoupSession *session,
}
g_object_unref (synchronizable);
+free_bundle:
+ ephy_sync_crypto_key_bundle_free (bundle);
free_node:
json_node_unref (node);
out:
@@ -1583,6 +1643,7 @@ ephy_sync_service_upload_synchronizable (EphySyncService *self,
EphySynchronizableManager *manager,
EphySynchronizable *synchronizable)
{
+ SyncCryptoKeyBundle *bundle;
SyncAsyncData *data;
char *bso;
char *endpoint;
@@ -1594,11 +1655,11 @@ ephy_sync_service_upload_synchronizable (EphySyncService *self,
g_assert (EPHY_IS_SYNCHRONIZABLE (synchronizable));
collection = ephy_synchronizable_manager_get_collection_name (manager);
- bso = ephy_synchronizable_to_bso (synchronizable,
- ephy_sync_service_get_key_bundle (self, collection));
+ bundle = ephy_sync_service_get_key_bundle (self, collection);
+ bso = ephy_synchronizable_to_bso (synchronizable, bundle);
if (!bso) {
g_warning ("Failed to convert synchronizable to BSO");
- return;
+ goto out;
}
id = ephy_synchronizable_get_id (synchronizable);
@@ -1612,6 +1673,8 @@ ephy_sync_service_upload_synchronizable (EphySyncService *self,
g_free (endpoint);
g_free (bso);
+out:
+ ephy_sync_crypto_key_bundle_free (bundle);
}
static void
@@ -1742,117 +1805,22 @@ ephy_sync_service_sync_collection (EphySyncService *self,
}
static void
-obtain_sync_key_bundles_cb (SoupSession *session,
- SoupMessage *msg,
- gpointer user_data)
+ephy_sync_service_stop_periodical_sync (EphySyncService *self)
{
- EphySyncService *self;
- SyncCryptoKeyBundle *bundle;
- JsonParser *parser;
- JsonObject *json;
- JsonObject *collections;
- JsonNode *node;
- JsonArray *array;
- JsonObjectIter iter;
- GError *error = NULL;
- GList *managers = NULL;
- const char *member;
- const char *payload;
- char *record;
- guint8 *kB;
- gboolean sync_finished = TRUE;
-
- self = ephy_shell_get_sync_service (ephy_shell_get_default ());
-
- if (msg->status_code != 200) {
- /* TODO: Generate and upload new sync key bundles. */
- g_warning ("Failed to get crypto/keys record. Status code: %u, response: %s",
- msg->status_code, msg->response_body->data);
- goto out;
- }
-
- parser = json_parser_new ();
- json_parser_load_from_data (parser, msg->response_body->data, -1, &error);
- if (error) {
- g_warning ("Response is not a valid JSON: %s", error->message);
- g_error_free (error);
- goto free_parser;
- }
- if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) {
- g_warning ("JSON root does not hold an array");
- goto free_parser;
- }
- json = json_node_get_object (json_parser_get_root (parser));
- if (!json_object_has_member (json, "payload")) {
- g_warning ("JSON object is missing 'payload' member");
- goto free_parser;
- }
- payload = json_object_get_string_member (json, "payload");
-
- /* Derive the Sync Key bundle from kB. The bundle consists of two 32 bytes keys:
- * the first one used as a symmetric encryption key (AES) and the second one
- * used as a HMAC key. */
- kB = ephy_sync_crypto_decode_hex (ephy_sync_service_get_token (self, TOKEN_KB));
- bundle = ephy_sync_crypto_derive_key_bundle (kB, TOKEN_LENGTH);
-
- record = ephy_sync_crypto_decrypt_record (payload, bundle);
- if (!record) {
- /* TODO: Notify user to sign in again. */
- g_warning ("Failed to decrypt crypto keys record");
- goto free_bundle;
- }
+ g_assert (EPHY_IS_SYNC_SERVICE (self));
- json_parser_load_from_data (parser, record, -1, &error);
- if (error) {
- g_warning ("Failed to parse JSON from record: %s", error->message);
- g_error_free (error);
- goto free_record;
- }
- if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) {
- g_warning ("JSON root does not hold a JSON object");
- goto free_record;
+ if (self->source_id != 0) {
+ g_source_remove (self->source_id);
+ self->source_id = 0;
}
- json = json_node_get_object (json_parser_get_root (parser));
+}
- /* Get the default key bundle. This must be always present. */
- if (!json_object_has_member (json, "default")) {
- g_warning ("Record is missing default keys");
- goto free_record;
- }
- if (!JSON_NODE_HOLDS_ARRAY (json_object_get_member (json, "default"))) {
- g_warning ("Default keys are not a JSON array");
- goto free_record;
- }
- array = json_object_get_array_member (json, "default");
- if (json_array_get_length (array) != 2) {
- g_warning ("Expected 2 default keys, found %u", json_array_get_length (array));
- goto free_record;
- }
-
- g_hash_table_insert (self->key_bundles,
- (char *)"default",
- ephy_sync_crypto_key_bundle_from_array (array));
-
- /* Get the per-collection key bundles, if any. */
- if (json_object_has_member (json, "collections")) {
- if (JSON_NODE_HOLDS_OBJECT (json_object_get_member (json, "collections"))) {
- collections = json_object_get_object_member (json, "collections");
- json_object_iter_init (&iter, collections);
- while (json_object_iter_next (&iter, &member, &node)) {
- if (!JSON_NODE_HOLDS_ARRAY (node))
- continue;
-
- array = json_node_get_array (node);
- if (json_array_get_length (array) == 2) {
- g_hash_table_insert (self->key_bundles,
- (char *)member,
- ephy_sync_crypto_key_bundle_from_array (array));
- }
- }
- }
- }
+static gboolean
+ephy_sync_service_sync (gpointer user_data)
+{
+ EphySyncService *self = EPHY_SYNC_SERVICE (user_data);
+ GList *managers = NULL;
- /* Successfully retrieved key bundles, sync collections. */
managers = ephy_shell_get_synchronizable_managers (ephy_shell_get_default ());
if (managers) {
guint num_managers = g_list_length (managers);
@@ -1864,32 +1832,33 @@ obtain_sync_key_bundles_cb (SoupSession *session,
index, num_managers);
g_list_free (managers);
- sync_finished = FALSE;
+ } else {
+ g_signal_emit (self, signals[SYNC_FINISHED], 0);
}
-free_record:
- g_free (record);
-free_bundle:
- ephy_sync_crypto_key_bundle_free (bundle);
- g_free (kB);
-free_parser:
- g_object_unref (parser);
-out:
- if (sync_finished)
- g_signal_emit (self, signals[SYNC_FINISHED], 0);
+ return G_SOURCE_CONTINUE;
+}
- ephy_sync_service_send_next_storage_request (self);
+static void
+ephy_sync_service_schedule_periodical_sync (EphySyncService *self)
+{
+ g_assert (EPHY_IS_SYNC_SERVICE (self));
+
+ self->source_id = g_timeout_add_seconds (g_settings_get_uint (EPHY_SETTINGS_SYNC,
+ EPHY_PREFS_SYNC_FREQUENCY) * 60,
+ ephy_sync_service_sync,
+ self);
+ LOG ("Scheduled new sync with frequency %u mins",
+ g_settings_get_uint (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_FREQUENCY));
}
static void
-ephy_sync_service_obtain_sync_key_bundles (EphySyncService *self)
+ephy_sync_service_sync_frequency_changed_cb (EphySyncService *self)
{
g_assert (EPHY_IS_SYNC_SERVICE (self));
- g_hash_table_remove_all (self->key_bundles);
- ephy_sync_service_queue_storage_request (self, "storage/crypto/keys",
- SOUP_METHOD_GET, NULL, -1, -1,
- obtain_sync_key_bundles_cb, NULL);
+ ephy_sync_service_stop_periodical_sync (self);
+ ephy_sync_service_schedule_periodical_sync (self);
}
void
diff --git a/src/sync/ephy-sync-service.h b/src/sync/ephy-sync-service.h
index 4687242..0d8590f 100644
--- a/src/sync/ephy-sync-service.h
+++ b/src/sync/ephy-sync-service.h
@@ -20,7 +20,6 @@
#pragma once
-#include "ephy-sync-crypto.h"
#include "ephy-synchronizable-manager.h"
#include <glib-object.h>
@@ -31,7 +30,8 @@ G_BEGIN_DECLS
typedef enum {
TOKEN_UID,
TOKEN_SESSIONTOKEN,
- TOKEN_KB
+ TOKEN_KB,
+ TOKEN_CRYPTOKEYS
} EphySyncTokenType;
#define EPHY_TYPE_SYNC_SERVICE (ephy_sync_service_get_type ())
@@ -50,8 +50,6 @@ void ephy_sync_service_set_token (EphySyncServi
EphySyncTokenType type);
const char *ephy_sync_service_token_name_from_type (EphySyncTokenType type);
EphySyncTokenType ephy_sync_service_token_type_from_name (const char *name);
-SyncCryptoKeyBundle *ephy_sync_service_get_key_bundle (EphySyncService *self,
- const char *collection);
void ephy_sync_service_clear_storage_credentials (EphySyncService *self);
void ephy_sync_service_clear_tokens (EphySyncService *self);
void ephy_sync_service_destroy_session (EphySyncService *self);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]