diff options
author | Gabriel Ivascu <ivascu.gabriel59@gmail.com> | 2017-06-13 19:11:16 +0300 |
---|---|---|
committer | Michael Catanzaro <mcatanzaro@igalia.com> | 2017-08-06 09:28:09 -0500 |
commit | ff057023a2c79c642271e334dd7c608d5efa26cc (patch) | |
tree | bf897d0c3eb23c6e55c9d6a5fad55d4edb154807 | |
parent | 41e319959f41eaaf76f13132582310f38db7cc8c (diff) | |
download | epiphany-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/README | 50 | ||||
-rw-r--r-- | lib/sync/ephy-sync-service.c | 114 |
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); |