[epiphany/wip/sync: 11/31] sync-service: Get crypto keys at sign in and store them in the secret schema



commit 9dc303ef736e84486c0c071c5a68a53773e5f607
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]