[epiphany/wip/ephy-sync: 50/52] sync-service: Get the storage token asynchronously
- From: Gabriel - Cristian Ivascu <gabrielivascu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/wip/ephy-sync: 50/52] sync-service: Get the storage token asynchronously
- Date: Tue, 26 Jul 2016 20:24:06 +0000 (UTC)
commit eff969c2068f95a11514eb459d42d40ee536ccc1
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date: Tue Jul 26 19:55:39 2016 +0300
sync-service: Get the storage token asynchronously
src/ephy-sync-service.c | 322 +++++++++++++++++++++++++----------------------
src/ephy-sync-service.h | 2 -
2 files changed, 169 insertions(+), 155 deletions(-)
---
diff --git a/src/ephy-sync-service.c b/src/ephy-sync-service.c
index ad72684..f30ef2a 100644
--- a/src/ephy-sync-service.c
+++ b/src/ephy-sync-service.c
@@ -28,11 +28,13 @@
#include <libsoup/soup.h>
#include <string.h>
-#define EMAIL_REGEX "^[a-z0-9]([a-z0-9.]+[a-z0-9])?@[a-z0-9.-]+$"
-#define TOKEN_SERVER_URL "https://token.services.mozilla.com/1.0/sync/1.5"
-#define FXA_BASEURL "https://api.accounts.firefox.com/"
-#define FXA_VERSION "v1/"
-#define STATUS_OK 200
+#define EMAIL_REGEX "^[a-z0-9]([a-z0-9.]+[a-z0-9])?@[a-z0-9.-]+$"
+#define TOKEN_SERVER_URL "https://token.services.mozilla.com/1.0/sync/1.5"
+#define FXA_BASEURL "https://api.accounts.firefox.com/"
+#define FXA_VERSION "v1/"
+#define STATUS_OK 200
+#define CERTIFICATE_DURATION (12 * 60 * 60)
+#define current_time_in_seconds (g_get_real_time () / 1000000)
struct _EphySyncService {
GObject parent_instance;
@@ -50,9 +52,12 @@ struct _EphySyncService {
gint64 last_auth_at;
gchar *certificate;
+ gint64 certificate_expiry_time;
+
gchar *storage_endpoint;
- gchar *token_server_id;
- gchar *token_server_key;
+ gchar *storage_token_id;
+ gchar *storage_token_key;
+ gint64 storage_token_expiry_time;
EphySyncCryptoRSAKeyPair *keypair;
};
@@ -64,14 +69,34 @@ build_json_string (const gchar *first_key,
const gchar *first_value,
...) G_GNUC_NULL_TERMINATED;
+static gboolean
+storage_token_is_expired (EphySyncService *self)
+{
+ if (self->storage_token_expiry_time == 0)
+ return TRUE;
+
+ /* Don't use storage tokens that are going to expire in less than 1 minute */
+ return self->certificate_expiry_time < current_time_in_seconds - 60;
+}
+
+static gboolean
+certificate_is_expired (EphySyncService *self)
+{
+ if (self->certificate_expiry_time == 0)
+ return TRUE;
+
+ /* Don't use certificates that are going to expire in less than 1 minute */
+ return self->certificate_expiry_time < current_time_in_seconds - 60;
+}
+
static guint
synchronous_hawk_post_request (EphySyncService *self,
- const gchar *endpoint,
- const gchar *id,
- guint8 *key,
- gsize key_length,
- gchar *request_body,
- JsonNode **node)
+ const gchar *endpoint,
+ const gchar *id,
+ guint8 *key,
+ gsize key_length,
+ gchar *request_body,
+ JsonNode **node)
{
EphySyncCryptoHawkOptions *hawk_options;
EphySyncCryptoHawkHeader *hawk_header;
@@ -320,6 +345,7 @@ check_certificate (EphySyncService *self,
}
self->last_auth_at = json_object_get_int_member (json, "fxa-lastAuthAt");
+ self->certificate_expiry_time = json_object_get_int_member (json, "exp") / 1000;
retval = TRUE;
out:
@@ -332,33 +358,144 @@ out:
return retval;
}
-static gboolean
-query_token_server (EphySyncService *self)
+static void
+sign_certificate (EphySyncService *self)
{
- SoupMessage *message;
+ EphySyncCryptoProcessedST *processed_st;
+ EphySyncCryptoRSAKeyPair *keypair;
+ JsonNode *node;
+ JsonObject *json;
+ gchar *tokenID;
+ gchar *public_key_json;
+ gchar *request_body;
+ gchar *n_str;
+ gchar *e_str;
+ const gchar *certificate;
+ guint status_code;
+
+ g_return_if_fail (self->sessionToken != NULL);
+
+ keypair = ephy_sync_crypto_generate_rsa_key_pair ();
+ g_return_if_fail (keypair != NULL);
+
+ processed_st = ephy_sync_crypto_process_session_token (self->sessionToken);
+ tokenID = ephy_sync_crypto_encode_hex (processed_st->tokenID, 0);
+
+ n_str = mpz_get_str (NULL, 10, keypair->public.n);
+ e_str = mpz_get_str (NULL, 10, keypair->public.e);
+ public_key_json = build_json_string ("algorithm", "RS",
+ "n", n_str,
+ "e", e_str,
+ NULL);
+ /* Duration is the lifetime of the certificate in milliseconds.
+ * The FxA server limits the duration to 24 hours.
+ */
+ request_body = g_strdup_printf ("{\"publicKey\": %s, \"duration\": %d}",
+ public_key_json, CERTIFICATE_DURATION * 1000);
+ status_code = synchronous_hawk_post_request (self,
+ "certificate/sign",
+ tokenID,
+ processed_st->reqHMACkey,
+ EPHY_SYNC_TOKEN_LENGTH,
+ request_body,
+ &node);
+ json = json_node_get_object (node);
+
+ if (status_code != STATUS_OK) {
+ g_warning ("FxA server errno: %ld, errmsg: %s",
+ json_object_get_int_member (json, "errno"),
+ json_object_get_string_member (json, "message"));
+ goto out;
+ }
+
+ /* Check if the certificate is valid */
+ certificate = json_object_get_string_member (json, "cert");
+ if (check_certificate (self, certificate) == FALSE) {
+ ephy_sync_crypto_rsa_key_pair_free (keypair);
+ goto out;
+ }
+
+ self->certificate = g_strdup (certificate);
+ self->keypair = keypair;
+
+out:
+ ephy_sync_crypto_processed_st_free (processed_st);
+ json_node_free (node);
+ g_free (tokenID);
+ g_free (public_key_json);
+ g_free (request_body);
+ g_free (n_str);
+ g_free (e_str);
+}
+
+static void
+token_server_response_cb (SoupSession *session,
+ SoupMessage *message,
+ gpointer user_data)
+{
+ EphySyncService *self;
JsonParser *parser;
- JsonNode *root;
JsonObject *json;
+ JsonObject *errors;
+ JsonArray *array;
+
+ self = EPHY_SYNC_SERVICE (user_data);
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, message->response_body->data, -1, NULL);
+ json = json_node_get_object (json_parser_get_root (parser));
+
+ if (message->status_code == STATUS_OK) {
+ self->storage_endpoint = g_strdup (json_object_get_string_member (json, "api_endpoint"));
+ self->storage_token_id = g_strdup (json_object_get_string_member (json, "id"));
+ self->storage_token_key = g_strdup (json_object_get_string_member (json, "key"));
+ self->storage_token_expiry_time = json_object_get_int_member (json, "duration") +
current_time_in_seconds;
+ } else if (message->status_code == 401) {
+ array = json_object_get_array_member (json, "errors");
+ errors = json_node_get_object (json_array_get_element (array, 0));
+
+ g_warning ("Failed to talk to the Token Server: %s: %s",
+ json_object_get_string_member (json, "status"),
+ json_object_get_string_member (errors, "description"));
+ } else {
+ g_warning ("Failed to talk to the Token Server, status code %u. "
+ "See https://docs.services.mozilla.com/token/apis.html#error-responses",
+ message->status_code);
+ }
+
+ g_object_unref (parser);
+}
+
+static void
+get_storage_token_from_token_server (EphySyncService *self)
+{
+ SoupMessage *message;
guint8 *kB = NULL;
gchar *hashed_kB = NULL;
gchar *client_state = NULL;
gchar *audience = NULL;
gchar *assertion = NULL;
gchar *authorization = NULL;
- gboolean retval = FALSE;
- g_return_val_if_fail (self->certificate != NULL, FALSE);
- g_return_val_if_fail (self->keypair != NULL, FALSE);
+ if (storage_token_is_expired (self) == FALSE)
+ return;
+
+ /* Sign another certificate is the current one has expired */
+ if (certificate_is_expired (self) == TRUE) {
+ g_free (self->certificate);
+ self->certificate_expiry_time = 0;
+ sign_certificate (self);
+ }
+
+ g_return_if_fail (self->certificate != NULL);
+ g_return_if_fail (self->keypair != NULL);
audience = get_audience_for_url (TOKEN_SERVER_URL);
assertion = ephy_sync_crypto_create_assertion (self->certificate,
audience,
5 * 60,
self->keypair);
- if (assertion == NULL) {
- g_warning ("Failed to create assertion");
- goto out;
- }
+ g_return_if_fail (assertion != NULL);
kB = ephy_sync_crypto_decode_hex (self->kB);
hashed_kB = g_compute_checksum_for_data (G_CHECKSUM_SHA256, kB, EPHY_SYNC_TOKEN_LENGTH);
@@ -367,71 +504,21 @@ query_token_server (EphySyncService *self)
message = soup_message_new (SOUP_METHOD_GET, TOKEN_SERVER_URL);
/* We need to add the X-Client-State header so that the Token Server will
- * recognize accounts that were previously used to sync Firefox data too. */
+ * recognize accounts that were previously used to sync Firefox data too.
+ */
soup_message_headers_append (message->request_headers,
"X-Client-State", client_state);
soup_message_headers_append (message->request_headers,
"authorization", authorization);
- soup_session_send_message (self->soup_session, message);
-
- parser = json_parser_new ();
- json_parser_load_from_data (parser,
- message->response_body->data,
- -1, NULL);
- root = json_parser_get_root (parser);
- json = json_node_get_object (root);
-
- if (message->status_code == STATUS_OK) {
- self->storage_endpoint = g_strdup (json_object_get_string_member (json, "api_endpoint"));
- self->token_server_id = g_strdup (json_object_get_string_member (json, "id"));
- self->token_server_key = g_strdup (json_object_get_string_member (json, "key"));
- } else if (message->status_code == 400) {
- g_warning ("Failed to talk to the Token Server: malformed request");
- goto unref;
- } else if (message->status_code == 401) {
- JsonArray *array;
- JsonNode *node;
- JsonObject *errors;
- const gchar *status;
- const gchar *description;
-
- status = json_object_get_string_member (json, "status");
- array = json_object_get_array_member (json, "errors");
- node = json_array_get_element (array, 0);
- errors = json_node_get_object (node);
- description = json_object_get_string_member (errors, "description");
-
- g_warning ("Failed to talk to the Token Server: %s: %s",
- status, description);
- goto unref;
- } else if (message->status_code == 404) {
- g_warning ("Failed to talk to the Token Server: unknown URL");
- goto unref;
- } else if (message->status_code == 405) {
- g_warning ("Failed to talk to the Token Server: unsupported method");
- goto unref;
- } else if (message->status_code == 406) {
- g_warning ("Failed to talk to the Token Server: unacceptable");
- goto unref;
- } else if (message->status_code == 503) {
- g_warning ("Failed to talk to the Token Server: service unavailable");
- goto unref;
- }
-
- retval = TRUE;
-
-unref:
- g_object_unref (parser);
+ soup_session_queue_message (self->soup_session, message,
+ token_server_response_cb, self);
-out:
g_free (kB);
g_free (hashed_kB);
g_free (client_state);
g_free (audience);
g_free (assertion);
g_free (authorization);
-
- return retval;
}
static void
@@ -439,7 +526,8 @@ ephy_sync_service_finalize (GObject *object)
{
EphySyncService *self = EPHY_SYNC_SERVICE (object);
- ephy_sync_crypto_rsa_key_pair_free (self->keypair);
+ if (self->keypair != NULL)
+ ephy_sync_crypto_rsa_key_pair_free (self->keypair);
G_OBJECT_CLASS (ephy_sync_service_parent_class)->finalize (object);
}
@@ -453,8 +541,8 @@ ephy_sync_service_dispose (GObject *object)
g_clear_pointer (&self->user_email, g_free);
g_clear_pointer (&self->certificate, g_free);
g_clear_pointer (&self->storage_endpoint, g_free);
- g_clear_pointer (&self->token_server_id, g_free);
- g_clear_pointer (&self->token_server_key, g_free);
+ g_clear_pointer (&self->storage_token_id, g_free);
+ g_clear_pointer (&self->storage_token_key, g_free);
ephy_sync_service_delete_all_tokens (self);
G_OBJECT_CLASS (ephy_sync_service_parent_class)->dispose (object);
@@ -727,75 +815,3 @@ out:
return retval;
}
-
-gboolean
-ephy_sync_service_sign_certificate (EphySyncService *self)
-{
- EphySyncCryptoProcessedST *processed_st;
- EphySyncCryptoRSAKeyPair *keypair;
- JsonNode *node;
- JsonObject *json;
- gchar *tokenID;
- gchar *public_key_json;
- gchar *request_body;
- gchar *n_str;
- gchar *e_str;
- const gchar *certificate;
- guint status_code;
- gboolean retval = FALSE;
-
- g_return_val_if_fail (self->sessionToken != NULL, FALSE);
-
- keypair = ephy_sync_crypto_generate_rsa_key_pair ();
- g_return_val_if_fail (keypair != NULL, FALSE);
-
- processed_st = ephy_sync_crypto_process_session_token (self->sessionToken);
- tokenID = ephy_sync_crypto_encode_hex (processed_st->tokenID, 0);
-
- n_str = mpz_get_str (NULL, 10, keypair->public.n);
- e_str = mpz_get_str (NULL, 10, keypair->public.e);
- public_key_json = build_json_string ("algorithm", "RS",
- "n", n_str,
- "e", e_str,
- NULL);
- /* The server allows a maximum certificate lifespan of 24 hours == 86400000 ms */
- request_body = g_strdup_printf ("{\"publicKey\": %s, \"duration\": 86400000}",
- public_key_json);
- status_code = synchronous_hawk_post_request (self,
- "certificate/sign",
- tokenID,
- processed_st->reqHMACkey,
- EPHY_SYNC_TOKEN_LENGTH,
- request_body,
- &node);
- json = json_node_get_object (node);
-
- if (status_code != STATUS_OK) {
- g_warning ("FxA server errno: %ld, errmsg: %s",
- json_object_get_int_member (json, "errno"),
- json_object_get_string_member (json, "message"));
- goto out;
- }
-
- /* Check if the certificate is valid */
- certificate = json_object_get_string_member (json, "cert");
- if (check_certificate (self, certificate) == FALSE) {
- ephy_sync_crypto_rsa_key_pair_free (keypair);
- goto out;
- }
-
- self->certificate = g_strdup (certificate);
- self->keypair = keypair;
- retval = TRUE;
-
-out:
- ephy_sync_crypto_processed_st_free (processed_st);
- json_node_free (node);
- g_free (tokenID);
- g_free (public_key_json);
- g_free (request_body);
- g_free (n_str);
- g_free (e_str);
-
- return retval;
-}
diff --git a/src/ephy-sync-service.h b/src/ephy-sync-service.h
index 94206e4..a76f661 100644
--- a/src/ephy-sync-service.h
+++ b/src/ephy-sync-service.h
@@ -67,8 +67,6 @@ gboolean ephy_sync_service_fetch_sync_keys (EphySyncService *self,
const gchar *keyFetchToken,
const gchar *unwrapBKey);
-gboolean ephy_sync_service_sign_certificate (EphySyncService *self);
-
G_END_DECLS
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]