summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Ivascu <ivascu.gabriel59@gmail.com>2017-06-13 19:11:16 +0300
committerMichael Catanzaro <mcatanzaro@igalia.com>2017-08-06 09:28:09 -0500
commitff057023a2c79c642271e334dd7c608d5efa26cc (patch)
treebf897d0c3eb23c6e55c9d6a5fad55d4edb154807
parent41e319959f41eaaf76f13132582310f38db7cc8c (diff)
downloadepiphany-ff057023a2c79c642271e334dd7c608d5efa26cc.tar.gz
sync-service: Make the sign in actions chronological
The flow is: 1. Retrieve the Master Sync Key. 2. Verify the storage version. 3. Retrieve the crypto keys. 4. Register the device. 5. Store the sync secrets.
-rw-r--r--lib/sync/README50
-rw-r--r--lib/sync/ephy-sync-service.c114
2 files changed, 123 insertions, 41 deletions
diff --git a/lib/sync/README b/lib/sync/README
index 95f73537d..e5186f240 100644
--- a/lib/sync/README
+++ b/lib/sync/README
@@ -262,6 +262,38 @@
keyFetchToken and unwrapBKey tokens amongst other. Now the client has
everything it needs and the sync can begin.
+ After the sync tokens have been received via fxaccounts:login command in the
+ preferences dialog, they are passed to EphySyncService via the _sign_in()
+ function. After that, EphySyncService takes care of the rest:
+
+ * It retrieves the Master Sync Key.
+
+ * It verifies the version of the Sync Storage Server. EphySyncService only
+ supports version 1.5 so the sign in will fail in case it detects a lower
+ version. The version is kept on the Sync Storage Server in the meta/global
+ record [18]. This is a special record that is not encrypted and contains
+ general information about the state of the Sync Storage Server. In case
+ the meta/global record is missing (this happens when the Firefox account
+ is newly created), EphySyncService will generate and upload new one.
+
+ * It retrieves account's crypto keys from the Sync Storage Server. In case
+ the crypto/keys record does not exist (this happens when the Firefox
+ account is newly created), EphySyncService will generate and upload a new
+ crypto/keys record that contains a randomly generated default key bundle.
+
+ * It registers the current device in the clients collection on the Sync
+ Storage Server.
+
+ * It stores the sync secrets (sessionToken, uid, Master Sync Key, crypto
+ keys) in the sync SecretSchema. They are kept there until sign out, when
+ the SecretSchema is cleared. At this point the sign in is considered
+ complete and the Firefox iframe is replaced with the panel that displays
+ the sync configuration options.
+
+ Note that the above steps are chronological. If any failure happens at any
+ point, EphySyncService will abort and report a sign in error in next to the
+ Firefox iframe.
+
Sync Modules in Epiphany
------------------------
@@ -277,24 +309,6 @@
gets certificates, destroys the session). It also schedules and makes
periodical synchronizations via every collection's manager.
- After the user clicks Sign In and the fxaccounts:login WebChannel message is
- received, the sessionToken, keyFetchToken and unwrapBKey are passed from the
- preferences dialog to EphySyncService via the _sign_in(). The sync service
- will then go through all the flow previously described to read the crypto/keys
- record from the Sync Storage Server. In case the crypto/keys record does not
- exist (this happens when the Firefox account is newly created), the sync
- service will generate and upload a new crypto/keys record which contains a
- randomly generated default key bundle. Note that EphySyncService currently
- supports only v1.5 of the Sync Storage Server and the sign in will fail if it
- detects a lower version. The version is kept on the Sync Storage Server in
- the meta/global record [18]. This is a special record that is not encrypted
- and contains general information about the state of the Sync Storage Server.
-
- The sessionToken, the Master Sync Key and the crypto key bundles are then
- stored in the sync SecretSchema. They are loaded in memory at every startup
- and used whenever needed until the user signs out. At that point they are
- freed and the SecretSchema is cleared.
-
The requests to the Sync Storage Server are sent internally via
ephy_sync_service_queue_storage_request(). This checks whether the storage
credentials are expired or not. If not expired, the request is sent directly
diff --git a/lib/sync/ephy-sync-service.c b/lib/sync/ephy-sync-service.c
index 64d70e696..601d3268c 100644
--- a/lib/sync/ephy-sync-service.c
+++ b/lib/sync/ephy-sync-service.c
@@ -41,6 +41,7 @@ struct _EphySyncService {
guint source_id;
char *user;
+ char *crypto_keys;
GHashTable *secrets;
GSList *managers;
@@ -1495,8 +1496,8 @@ store_secrets_cb (SecretService *service,
ephy_sync_service_destroy_session (self, NULL);
g_hash_table_remove_all (self->secrets);
} else {
+ LOG ("Successfully stored sync secrets");
ephy_sync_utils_set_sync_user (self->user);
- ephy_sync_service_register_device (self, NULL);
}
g_signal_emit (self, signals[STORE_FINISHED], 0, error);
@@ -1539,6 +1540,7 @@ ephy_sync_service_store_secrets (EphySyncService *self)
/* Translators: %s is the email of the user. */
label = g_strdup_printf (_("The sync secrets of %s"), self->user);
+ LOG ("Storing sync secrets...");
secret_service_store (NULL, EPHY_SYNC_SECRET_SCHEMA,
attributes, NULL, label, secret, NULL,
(GAsyncReadyCallback)store_secrets_cb, self);
@@ -1561,6 +1563,7 @@ ephy_sync_service_dispose (GObject *object)
ephy_sync_service_clear_storage_credentials (self);
g_clear_object (&self->session);
+ g_clear_pointer (&self->crypto_keys, g_free);
g_clear_pointer (&self->key_pair, ephy_sync_crypto_rsa_key_pair_free);
g_clear_pointer (&self->secrets, g_hash_table_destroy);
g_clear_pointer (&self->managers, g_slist_free);
@@ -1651,14 +1654,35 @@ ephy_sync_service_new (gboolean sync_periodically)
NULL));
}
-static char *
+static void
+upload_crypto_keys_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ EphySyncService *self = EPHY_SYNC_SERVICE (user_data);
+
+ if (msg->status_code != 200) {
+ g_warning ("Failed to upload crypto/keys record. Status code: %u, response: %s",
+ msg->status_code, msg->response_body->data);
+ ephy_sync_service_report_sign_in_error (self,
+ _("Failed to upload crypto/keys record."),
+ NULL, TRUE);
+ } else {
+ LOG ("Successfully uploaded crypto/keys record");
+ ephy_sync_service_set_secret (self, secrets[CRYPTO_KEYS], self->crypto_keys);
+ ephy_sync_service_register_device (self, NULL);
+ }
+
+ g_clear_pointer (&self->crypto_keys, g_free);
+}
+
+static void
ephy_sync_service_upload_crypto_keys (EphySyncService *self)
{
SyncCryptoKeyBundle *bundle;
JsonNode *node;
JsonObject *record;
- char *payload_clear;
- char *payload_cipher;
+ char *payload;
char *body;
const char *kb_hex;
guint8 *kb;
@@ -1669,27 +1693,25 @@ ephy_sync_service_upload_crypto_keys (EphySyncService *self)
node = json_node_new (JSON_NODE_OBJECT);
record = json_object_new ();
- payload_clear = ephy_sync_crypto_generate_crypto_keys ();
+ self->crypto_keys = ephy_sync_crypto_generate_crypto_keys ();
kb = ephy_sync_utils_decode_hex (kb_hex);
bundle = ephy_sync_crypto_derive_master_bundle (kb);
- payload_cipher = ephy_sync_crypto_encrypt_record (payload_clear, bundle);
- json_object_set_string_member (record, "payload", payload_cipher);
+ payload = ephy_sync_crypto_encrypt_record (self->crypto_keys, bundle);
+ json_object_set_string_member (record, "payload", payload);
json_object_set_string_member (record, "id", "keys");
json_node_set_object (node, record);
body = json_to_string (node, FALSE);
ephy_sync_service_queue_storage_request (self, "storage/crypto/keys",
- SOUP_METHOD_PUT, body,
- -1, -1, NULL, NULL);
+ SOUP_METHOD_PUT, body, -1, -1,
+ upload_crypto_keys_cb, self);
g_free (body);
- g_free (payload_cipher);
+ g_free (payload);
g_free (kb);
json_object_unref (record);
json_node_unref (node);
ephy_sync_crypto_key_bundle_free (bundle);
-
- return payload_clear;
}
static void
@@ -1703,12 +1725,13 @@ get_crypto_keys_cb (SoupSession *session,
JsonObject *json = NULL;
GError *error = NULL;
const char *payload;
- char *crypto_keys = NULL;
+ char *crypto_keys;
guint8 *kb = NULL;
if (msg->status_code == 404) {
- crypto_keys = ephy_sync_service_upload_crypto_keys (self);
- goto store_secrets;
+ LOG ("crypto/keys record not found, uploading new one...");
+ ephy_sync_service_upload_crypto_keys (self);
+ return;
}
if (msg->status_code != 200) {
@@ -1744,10 +1767,10 @@ get_crypto_keys_cb (SoupSession *session,
goto out_error;
}
-store_secrets:
ephy_sync_service_set_secret (self, secrets[CRYPTO_KEYS], crypto_keys);
- ephy_sync_service_store_secrets (self);
+ ephy_sync_service_register_device (self, NULL);
goto out_no_error;
+
out_error:
ephy_sync_service_report_sign_in_error (self, _("Failed to retrieve crypto keys."),
NULL, TRUE);
@@ -1767,6 +1790,7 @@ ephy_sync_service_get_crypto_keys (EphySyncService *self)
{
g_assert (EPHY_IS_SYNC_SERVICE (self));
+ LOG ("Getting account's crypto keys...");
ephy_sync_service_queue_storage_request (self, "storage/crypto/keys",
SOUP_METHOD_GET, NULL, -1, -1,
get_crypto_keys_cb, self);
@@ -1789,6 +1813,25 @@ make_engine_object (int version)
}
static void
+upload_meta_global_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ EphySyncService *self = EPHY_SYNC_SERVICE (user_data);
+
+ if (msg->status_code != 200) {
+ g_warning ("Failed to upload meta/global record. Status code: %u, response: %s",
+ msg->status_code, msg->response_body->data);
+ ephy_sync_service_report_sign_in_error (self,
+ _("Failed to upload meta/global record."),
+ NULL, TRUE);
+ } else {
+ LOG ("Successfully uploaded meta/global record");
+ ephy_sync_service_get_crypto_keys (self);
+ }
+}
+
+static void
ephy_sync_service_upload_meta_global (EphySyncService *self)
{
JsonNode *node;
@@ -1827,8 +1870,8 @@ ephy_sync_service_upload_meta_global (EphySyncService *self)
body = json_to_string (node, FALSE);
ephy_sync_service_queue_storage_request (self, "storage/meta/global",
- SOUP_METHOD_PUT, body,
- -1, -1, NULL, NULL);
+ SOUP_METHOD_PUT, body, -1, -1,
+ upload_meta_global_cb, self);
g_free (body);
g_free (payload_str);
@@ -1852,8 +1895,9 @@ verify_storage_version_cb (SoupSession *session,
int storage_version;
if (msg->status_code == 404) {
+ LOG ("meta/global record not found, uploading new one...");
ephy_sync_service_upload_meta_global (self);
- goto obtain_crypto_keys;
+ return;
}
if (msg->status_code != 200) {
@@ -1902,9 +1946,9 @@ verify_storage_version_cb (SoupSession *session,
goto out_error;
}
-obtain_crypto_keys:
ephy_sync_service_get_crypto_keys (self);
goto out_no_error;
+
out_error:
message = message ? message : _("Failed to verify storage version.");
ephy_sync_service_report_sign_in_error (self, message, NULL, TRUE);
@@ -1922,6 +1966,7 @@ ephy_sync_service_verify_storage_version (EphySyncService *self)
{
g_assert (EPHY_IS_SYNC_SERVICE (self));
+ LOG ("Verifying account's storage version...");
ephy_sync_service_queue_storage_request (self, "storage/meta/global",
SOUP_METHOD_GET, NULL, -1, -1,
verify_storage_version_cb, self);
@@ -2074,6 +2119,7 @@ ephy_sync_service_sign_in (EphySyncService *self,
session_token, unwrap_kb,
token_id_hex, req_hmac_key,
resp_hmac_key, resp_xor_key);
+ LOG ("Getting account's Sync Key...");
ephy_sync_service_fxa_hawk_get (self, "account/keys",
token_id_hex, req_hmac_key, 32,
get_account_keys_cb, data);
@@ -2146,6 +2192,26 @@ ephy_sync_service_unregister_manager (EphySyncService *self,
g_signal_handlers_disconnect_by_func (manager, synchronizable_modified_cb, self);
}
+static void
+register_device_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ EphySyncService *self = EPHY_SYNC_SERVICE (user_data);
+
+ if (msg->status_code != 200) {
+ g_warning ("Failed to register device. Status code: %u, response: %s",
+ msg->status_code, msg->response_body->data);
+ ephy_sync_service_report_sign_in_error (self,
+ _("Failed to register device."),
+ NULL, TRUE);
+ } else {
+ LOG ("Successfully registered device");
+ if (self->is_signing_in)
+ ephy_sync_service_store_secrets (self);
+ }
+}
+
void
ephy_sync_service_register_device (EphySyncService *self,
const char *device_name)
@@ -2205,9 +2271,11 @@ ephy_sync_service_register_device (EphySyncService *self,
body = json_to_string (node, FALSE);
/* Upload BSO and store the new device ID and name. */
+ LOG ("Registering device with name '%s'...", name);
endpoint = g_strdup_printf ("storage/clients/%s", id);
- ephy_sync_service_queue_storage_request (self, endpoint, SOUP_METHOD_PUT,
- body, -1, -1, NULL, NULL);
+ ephy_sync_service_queue_storage_request (self, endpoint,
+ SOUP_METHOD_PUT, body, -1, -1,
+ register_device_cb, self);
ephy_sync_utils_set_device_id (id);
ephy_sync_utils_set_device_name (name);