[epiphany/wip/sync: 56/86] sync: Obtain the default sync keys at sign in
- From: Gabriel Ivașcu <gabrielivascu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/wip/sync: 56/86] sync: Obtain the default sync keys at sign in
- Date: Wed, 22 Mar 2017 15:34:38 +0000 (UTC)
commit 92517c54e8ec6826f09b99483be87eee8ebc3a3d
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date: Thu Mar 9 18:27:28 2017 +0200
sync: Obtain the default sync keys at sign in
src/sync/ephy-sync-crypto.c | 45 +++++++++++++++
src/sync/ephy-sync-crypto.h | 6 ++
src/sync/ephy-sync-secret.c | 2 +
src/sync/ephy-sync-service.c | 126 +++++++++++++++++++++++++++++++++++++++++-
src/sync/ephy-sync-utils.c | 8 +++
src/sync/ephy-sync-utils.h | 4 +-
6 files changed, 189 insertions(+), 2 deletions(-)
---
diff --git a/src/sync/ephy-sync-crypto.c b/src/sync/ephy-sync-crypto.c
index fc16156..3984070 100644
--- a/src/sync/ephy-sync-crypto.c
+++ b/src/sync/ephy-sync-crypto.c
@@ -653,6 +653,51 @@ ephy_sync_crypto_derive_master_keys (const guint8 *kB,
g_free (hmac_key_hex);
}
+gboolean
+ephy_sync_crypto_sha256_hmac_is_valid (const char *text,
+ const guint8 *key,
+ const char *expected)
+{
+ char *hmac;
+ gboolean retval;
+
+ g_return_val_if_fail (text, FALSE);
+ g_return_val_if_fail (key, FALSE);
+ g_return_val_if_fail (expected, FALSE);
+
+ /* SHA256 expects a 32 bytes key. */
+ hmac = g_compute_hmac_for_string (G_CHECKSUM_SHA256, key, 32, text, -1);
+ retval = g_strcmp0 (hmac, expected) == 0;
+ g_free (hmac);
+
+ return retval;
+}
+
+char *
+ephy_sync_crypto_decrypt_record (const char *ciphertext_b64,
+ const char *iv_b64,
+ const guint8 *aes_key)
+{
+ char *decrypted;
+ guint8 *ciphertext;
+ guint8 *iv;
+ gsize ciphertext_len;
+ gsize iv_len;
+
+ g_return_val_if_fail (ciphertext_b64, NULL);
+ g_return_val_if_fail (iv_b64, NULL);
+ g_return_val_if_fail (aes_key, NULL);
+
+ ciphertext = g_base64_decode (ciphertext_b64, &ciphertext_len);
+ iv = g_base64_decode (iv_b64, &iv_len);
+ decrypted = ephy_sync_crypto_aes_256_decrypt (ciphertext, ciphertext_len, aes_key, iv);
+
+ g_free (ciphertext);
+ g_free (iv);
+
+ return decrypted;
+}
+
SyncCryptoHawkHeader *
ephy_sync_crypto_compute_hawk_header (const char *url,
const char *method,
diff --git a/src/sync/ephy-sync-crypto.h b/src/sync/ephy-sync-crypto.h
index 4a02178..6f957a2 100644
--- a/src/sync/ephy-sync-crypto.h
+++ b/src/sync/ephy-sync-crypto.h
@@ -97,6 +97,12 @@ void ephy_sync_crypto_compute_sync_keys (const char
void ephy_sync_crypto_derive_master_keys (const guint8 *kB,
guint8 **aes_key,
guint8 **hmac_key);
+gboolean ephy_sync_crypto_sha256_hmac_is_valid (const char *text,
+ const guint8 *key,
+ const char *expected);
+char *ephy_sync_crypto_decrypt_record (const char *ciphertext_b64,
+ const char *iv_b64,
+ const guint8 *aes_key);
SyncCryptoHawkHeader *ephy_sync_crypto_compute_hawk_header (const char *url,
const char *method,
const char *id,
diff --git a/src/sync/ephy-sync-secret.c b/src/sync/ephy-sync-secret.c
index 2db6fde..a09af8b 100644
--- a/src/sync/ephy-sync-secret.c
+++ b/src/sync/ephy-sync-secret.c
@@ -224,6 +224,8 @@ ephy_sync_secret_store_tokens (EphySyncService *service)
"unwrapBKey", ephy_sync_service_get_token (service,
TOKEN_UNWRAPBKEY),
"kA", ephy_sync_service_get_token (service, TOKEN_KA),
"kB", ephy_sync_service_get_token (service, TOKEN_KB),
+ "defaultAESKey", ephy_sync_service_get_token (service,
TOKEN_DEFAULT_AES_KEY),
+ "defaultHMACKey", ephy_sync_service_get_token (service,
TOKEN_DEFAULT_HMAC_KEY),
NULL);
value = secret_value_new (tokens, -1, "text/plain");
attributes = secret_attributes_build (EPHY_SYNC_TOKEN_SCHEMA, EMAIL_KEY,
diff --git a/src/sync/ephy-sync-service.c b/src/sync/ephy-sync-service.c
index c2c905f..fae70d7 100644
--- a/src/sync/ephy-sync-service.c
+++ b/src/sync/ephy-sync-service.c
@@ -55,6 +55,8 @@ struct _EphySyncService {
char *unwrapBKey;
char *kA;
char *kB;
+ char *default_aes_key;
+ char *default_hmac_key;
char *user_email;
double sync_time;
@@ -768,6 +770,10 @@ ephy_sync_service_get_token (EphySyncService *self,
return self->kA;
case TOKEN_KB:
return self->kB;
+ case TOKEN_DEFAULT_AES_KEY:
+ return self->default_aes_key;
+ case TOKEN_DEFAULT_HMAC_KEY:
+ return self->default_hmac_key;
default:
g_assert_not_reached ();
}
@@ -806,6 +812,14 @@ ephy_sync_service_set_token (EphySyncService *self,
g_free (self->kB);
self->kB = g_strdup (value);
break;
+ case TOKEN_DEFAULT_AES_KEY:
+ g_free (self->default_aes_key);
+ self->default_aes_key = g_strdup (value);
+ break;
+ case TOKEN_DEFAULT_HMAC_KEY:
+ g_free (self->default_hmac_key);
+ self->default_hmac_key = g_strdup (value);
+ break;
default:
g_assert_not_reached ();
}
@@ -834,6 +848,8 @@ ephy_sync_service_clear_tokens (EphySyncService *self)
g_clear_pointer (&self->unwrapBKey, g_free);
g_clear_pointer (&self->kA, g_free);
g_clear_pointer (&self->kB, g_free);
+ g_clear_pointer (&self->default_aes_key, g_free);
+ g_clear_pointer (&self->default_hmac_key, g_free);
}
static void
@@ -908,6 +924,112 @@ ephy_sync_service_destroy_session (EphySyncService *self,
}
static void
+obtain_default_sync_keys_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ EphySyncService *service;
+ JsonParser *parser;
+ JsonObject *json;
+ JsonArray *array;
+ const char *ciphertext_b64;
+ const char *iv_b64;
+ const char *hmac;
+ char *payload;
+ char *record;
+ char *default_aes_key_hex;
+ char *default_hmac_key_hex;
+ guint8 *kB;
+ guint8 *aes_key;
+ guint8 *hmac_key;
+ guint8 *default_aes_key;
+ guint8 *default_hmac_key;
+ gsize len;
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, msg->response_body->data, -1, NULL);
+ json = json_node_get_object (json_parser_get_root (parser));
+
+ if (msg->status_code != 200) {
+ g_warning ("Failed to get crypto/keys record: errno: %ld, errmsg: %s",
+ json_object_get_int_member (json, "errno"),
+ json_object_get_string_member (json, "message"));
+ goto out;
+ }
+
+ service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+ payload = g_strdup (json_object_get_string_member (json, "payload"));
+ json_parser_load_from_data (parser, payload, -1, NULL);
+ json = json_node_get_object (json_parser_get_root (parser));
+ ciphertext_b64 = json_object_get_string_member (json, "ciphertext");
+ iv_b64 = json_object_get_string_member (json, "IV");
+ hmac = json_object_get_string_member (json, "hmac");
+
+ /* 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 (service, TOKEN_KB));
+ ephy_sync_crypto_derive_master_keys (kB, &aes_key, &hmac_key);
+
+ /* Under no circumstances should a client try to decrypt a record if the HMAC
+ * verification fails. If the verification is successful, proceed to decrypt
+ * the record and retrieve the default sync keys. Otherwise, signal the error
+ * to the user.*/
+ if (!ephy_sync_crypto_sha256_hmac_is_valid (ciphertext_b64, hmac_key, hmac)) {
+ g_signal_emit (service, signals[SIGN_IN_ERROR], 0, _("Failed to obtain the default sync keys"));
+ ephy_sync_service_destroy_session (service, NULL);
+ ephy_sync_service_set_user_email (service, NULL);
+ ephy_sync_service_clear_tokens (service);
+ g_settings_set_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_SYNC_USER, "");
+ LOG ("Failed to verify the HMAC value of the crypto/keys record");
+ } else {
+ record = ephy_sync_crypto_decrypt_record (ciphertext_b64, iv_b64, aes_key);
+ json_parser_load_from_data (parser, record, -1, NULL);
+ json = json_node_get_object (json_parser_get_root (parser));
+ array = json_object_get_array_member (json, "default");
+
+ /* TODO: Handle the case when the decrypted record has a non-empty "collections" member.
+ * https://github.com/michielbdejong/fxsync-webcrypto/issues/19 */
+
+ default_aes_key = g_base64_decode (json_array_get_string_element (array, 0), &len);
+ default_hmac_key = g_base64_decode (json_array_get_string_element (array, 1), &len);
+ default_aes_key_hex = ephy_sync_crypto_encode_hex (default_aes_key, 0);
+ default_hmac_key_hex = ephy_sync_crypto_encode_hex (default_hmac_key, 0);
+ ephy_sync_service_set_token (service, default_aes_key_hex, TOKEN_DEFAULT_AES_KEY);
+ ephy_sync_service_set_token (service, default_hmac_key_hex, TOKEN_DEFAULT_HMAC_KEY);
+
+ /* Everything is OK, store the tokens in the secret schema. */
+ ephy_sync_secret_store_tokens (service);
+
+ g_free (record);
+ g_free (default_aes_key);
+ g_free (default_hmac_key);
+ g_free (default_aes_key_hex);
+ g_free (default_hmac_key_hex);
+ }
+
+ g_free (payload);
+ g_free (kB);
+ g_free (aes_key);
+ g_free (hmac_key);
+
+out:
+ g_object_unref (parser);
+
+ ephy_sync_service_send_next_storage_request (service);
+}
+
+static void
+ephy_sync_service_obtain_default_sync_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_default_sync_keys_cb, NULL);
+}
+
+static void
check_storage_version_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
@@ -935,8 +1057,10 @@ check_storage_version_cb (SoupSession *session,
json = json_node_get_object (json_parser_get_root (parser));
storage_version = json_object_get_int_member (json, "storageVersion");
+ /* If the storage version is correct, proceed to obtain the default sync keys.
+ * Otherwise, signal the error to the user. */
if (storage_version == STORAGE_VERSION) {
- ephy_sync_secret_store_tokens (service);
+ ephy_sync_service_obtain_default_sync_keys (service);
} else {
/* Translators: the %d is the storage version, the \n is a newline character. */
char *message = g_strdup_printf (_("Your Firefox Account uses a storage version "
diff --git a/src/sync/ephy-sync-utils.c b/src/sync/ephy-sync-utils.c
index 03e345a..496903d 100644
--- a/src/sync/ephy-sync-utils.c
+++ b/src/sync/ephy-sync-utils.c
@@ -107,6 +107,10 @@ ephy_sync_utils_token_name_from_type (EphySyncTokenType type)
return "kA";
case TOKEN_KB:
return "kB";
+ case TOKEN_DEFAULT_AES_KEY:
+ return "defaultAESKey";
+ case TOKEN_DEFAULT_HMAC_KEY:
+ return "defaultHMACKey";
default:
g_assert_not_reached ();
}
@@ -127,6 +131,10 @@ ephy_sync_utils_token_type_from_name (const char *name)
return TOKEN_KA;
} else if (!g_strcmp0 (name, "kB")) {
return TOKEN_KB;
+ } else if (!g_strcmp0 (name, "defaultAESKey")) {
+ return TOKEN_DEFAULT_AES_KEY;
+ } else if (!g_strcmp0 (name, "defaultHMACKey")) {
+ return TOKEN_DEFAULT_HMAC_KEY;
} else {
g_assert_not_reached ();
}
diff --git a/src/sync/ephy-sync-utils.h b/src/sync/ephy-sync-utils.h
index 4dd8d5d..0ae3f2f 100644
--- a/src/sync/ephy-sync-utils.h
+++ b/src/sync/ephy-sync-utils.h
@@ -28,7 +28,9 @@ typedef enum {
TOKEN_KEYFETCHTOKEN,
TOKEN_UNWRAPBKEY,
TOKEN_KA,
- TOKEN_KB
+ TOKEN_KB,
+ TOKEN_DEFAULT_AES_KEY,
+ TOKEN_DEFAULT_HMAC_KEY
} EphySyncTokenType;
G_BEGIN_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]