[epiphany/wip/sync: 66/86] sync-crypto: Modify _decrypt_record()
- From: Gabriel Ivașcu <gabrielivascu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/wip/sync: 66/86] sync-crypto: Modify _decrypt_record()
- Date: Tue, 28 Mar 2017 20:59:27 +0000 (UTC)
commit adbc6d677020ded0ea42b4844a465f6cc78c4ad1
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date: Thu Mar 16 00:53:33 2017 +0200
sync-crypto: Modify _decrypt_record()
src/sync/ephy-sync-crypto.c | 151 ++++++++++++++++++++++++++++--------------
src/sync/ephy-sync-crypto.h | 13 +---
src/sync/ephy-sync-service.c | 36 +++-------
3 files changed, 115 insertions(+), 85 deletions(-)
---
diff --git a/src/sync/ephy-sync-crypto.c b/src/sync/ephy-sync-crypto.c
index a070f5c..4524875 100644
--- a/src/sync/ephy-sync-crypto.c
+++ b/src/sync/ephy-sync-crypto.c
@@ -618,9 +618,9 @@ ephy_sync_crypto_aes_256_decrypt (const guint8 *data,
char *unpadded;
struct CBC_CTX(struct aes256_ctx, AES_BLOCK_SIZE) ctx;
- g_return_val_if_fail (data, NULL);
- g_return_val_if_fail (key, NULL);
- g_return_val_if_fail (iv, NULL);
+ g_assert (data);
+ g_assert (key);
+ g_assert (iv);
decrypted = g_malloc (data_len);
@@ -634,6 +634,26 @@ ephy_sync_crypto_aes_256_decrypt (const guint8 *data,
return unpadded;
}
+static gboolean
+ephy_sync_crypto_hmac_is_valid (const char *text,
+ const guint8 *key,
+ const char *expected)
+{
+ char *hmac;
+ gboolean retval;
+
+ g_assert (text);
+ g_assert (key);
+ g_assert (expected);
+
+ /* 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;
+}
+
void
ephy_sync_crypto_process_key_fetch_token (const char *keyFetchToken,
guint8 **tokenID,
@@ -785,99 +805,128 @@ ephy_sync_crypto_compute_sync_keys (const char *bundle,
g_free (respMAC2_hex);
}
-void
-ephy_sync_crypto_derive_master_keys (const guint8 *kB,
- guint8 **aes_key,
- guint8 **hmac_key)
+SyncCryptoKeyBundle *
+ephy_sync_crypto_derive_key_bundle (const guint8 *key,
+ gsize key_len)
{
+ SyncCryptoKeyBundle *bundle;
guint8 *salt;
guint8 *prk;
guint8 *tmp;
+ guint8 *aes_key;
char *prk_hex;
char *aes_key_hex;
char *hmac_key_hex;
const char *info = "identity.mozilla.com/picl/v1/oldsync";
- g_return_if_fail (kB);
- g_return_if_fail (aes_key);
- g_return_if_fail (hmac_key);
+ g_return_val_if_fail (key, NULL);
+ g_return_val_if_fail (key_len > 0, NULL);
/* Perform a two step HKDF with an all-zeros salt.
* T(1) will represent the AES key, T(2) will represent the HMAC key. */
- salt = g_malloc0 (EPHY_SYNC_TOKEN_LENGTH);
+ salt = g_malloc0 (key_len);
prk_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256,
- salt, EPHY_SYNC_TOKEN_LENGTH,
- kB, EPHY_SYNC_TOKEN_LENGTH);
+ salt, key_len,
+ key, key_len);
prk = ephy_sync_crypto_decode_hex (prk_hex);
tmp = ephy_sync_crypto_concat_bytes ((guint8 *)info, strlen (info),
"\x01", 1,
NULL);
aes_key_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256,
- prk, EPHY_SYNC_TOKEN_LENGTH,
+ prk, key_len,
tmp, strlen (info) + 1);
- *aes_key = ephy_sync_crypto_decode_hex (aes_key_hex);
+ aes_key = ephy_sync_crypto_decode_hex (aes_key_hex);
g_free (tmp);
- tmp = ephy_sync_crypto_concat_bytes (*aes_key, EPHY_SYNC_TOKEN_LENGTH,
+ tmp = ephy_sync_crypto_concat_bytes (aes_key, key_len,
(guint8 *)info, strlen (info),
"\x02", 1,
NULL);
hmac_key_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256,
- prk, EPHY_SYNC_TOKEN_LENGTH,
- tmp, EPHY_SYNC_TOKEN_LENGTH + strlen (info) + 1);
- *hmac_key = ephy_sync_crypto_decode_hex (hmac_key_hex);
+ prk, key_len,
+ tmp, key_len + strlen (info) + 1);
+ bundle = ephy_sync_crypto_key_bundle_new (aes_key_hex, hmac_key_hex);
- g_free (salt);
- g_free (prk_hex);
- g_free (prk);
+ g_free (hmac_key_hex);
g_free (tmp);
g_free (aes_key_hex);
- 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);
+ g_free (prk);
+ g_free (prk_hex);
+ g_free (salt);
- return retval;
+ return bundle;
}
char *
-ephy_sync_crypto_decrypt_record (const char *ciphertext_b64,
- const char *iv_b64,
- const guint8 *aes_key)
+ephy_sync_crypto_decrypt_record (const char *payload,
+ SyncCryptoKeyBundle *bundle)
{
- char *decrypted;
+ JsonParser *parser;
+ JsonObject *json;
+ guint8 *aes_key;
+ guint8 *hmac_key;
guint8 *ciphertext;
guint8 *iv;
+ char *cleartext = NULL;
+ const char *ciphertext_b64;
+ const char *iv_b64;
+ const char *hmac;
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);
+ g_return_val_if_fail (payload, NULL);
+ g_return_val_if_fail (bundle, NULL);
+ /* Extract ciphertext, iv and hmac from payload. */
+ parser = json_parser_new ();
+ if (!json_parser_load_from_data (parser, payload, -1, NULL)) {
+ g_warning ("Payload is not a valid JSON");
+ goto free_parser;
+ }
+ if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) {
+ g_warning ("JSON node does not hold a JSON object");
+ goto free_parser;
+ }
+ json = json_node_get_object (json_parser_get_root (parser));
+ if (!json_object_has_member (json, "ciphertext") ||
+ !json_object_has_member (json, "IV") ||
+ !json_object_has_member (json, "hmac")) {
+ g_warning ("JSON object has missing members");
+ goto free_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");
+
+ /* Get the encryption key and the HMAC key. */
+ aes_key = ephy_sync_crypto_decode_hex (bundle->aes_key_hex);
+ hmac_key = ephy_sync_crypto_decode_hex (bundle->hmac_key_hex);
+ if (!aes_key || !hmac_key) {
+ g_warning ("The key bundle does not hold valid keys");
+ goto free_keys;
+ }
+
+ /* Under no circumstances should a client try to decrypt a record
+ * if the HMAC verification fails. */
+ if (!ephy_sync_crypto_hmac_is_valid (ciphertext_b64, hmac_key, hmac)) {
+ g_warning ("Incorrect HMAC value");
+ goto free_keys;
+ }
+
+ /* Finally, decrypt the record. */
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);
+ cleartext = ephy_sync_crypto_aes_256_decrypt (ciphertext, ciphertext_len, aes_key, iv);
g_free (ciphertext);
g_free (iv);
+free_keys:
+ g_free (aes_key);
+ g_free (hmac_key);
+free_parser:
+ g_object_unref (parser);
- return decrypted;
+ return cleartext;
}
SyncCryptoHawkHeader *
diff --git a/src/sync/ephy-sync-crypto.h b/src/sync/ephy-sync-crypto.h
index a7e0b85..7250b66 100644
--- a/src/sync/ephy-sync-crypto.h
+++ b/src/sync/ephy-sync-crypto.h
@@ -102,15 +102,10 @@ void ephy_sync_crypto_compute_sync_keys (const char
const guint8 *unwrapBKey,
guint8 **kA,
guint8 **kB);
-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);
+SyncCryptoKeyBundle *ephy_sync_crypto_derive_key_bundle (const guint8 *key,
+ gsize key_len);
+char *ephy_sync_crypto_decrypt_record (const char *payload,
+ SyncCryptoKeyBundle *bundle);
SyncCryptoHawkHeader *ephy_sync_crypto_compute_hawk_header (const char *url,
const char *method,
const char *id,
diff --git a/src/sync/ephy-sync-service.c b/src/sync/ephy-sync-service.c
index 1072319..a1064d1 100644
--- a/src/sync/ephy-sync-service.c
+++ b/src/sync/ephy-sync-service.c
@@ -910,21 +910,17 @@ obtain_sync_key_bundles (SoupSession *session,
gpointer user_data)
{
EphySyncService *service;
+ SyncCryptoKeyBundle *bundle;
JsonParser *parser;
JsonObject *json;
JsonObject *collections;
JsonNode *node;
JsonArray *array;
JsonObjectIter iter;
- const char *ciphertext_b64;
- const char *iv_b64;
- const char *hmac;
const char *member;
- char *payload;
+ const char *payload;
char *record;
guint8 *kB;
- guint8 *aes_key;
- guint8 *hmac_key;
service = ephy_shell_get_sync_service (ephy_shell_get_default ());
@@ -937,27 +933,16 @@ obtain_sync_key_bundles (SoupSession *session,
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));
- 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");
+ 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 (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_warning ("Failed to verify the HMAC value of the crypto/keys record");
- } else {
- record = ephy_sync_crypto_decrypt_record (ciphertext_b64, iv_b64, aes_key);
+ bundle = ephy_sync_crypto_derive_key_bundle (kB, EPHY_SYNC_TOKEN_LENGTH);
+
+ record = ephy_sync_crypto_decrypt_record (payload, bundle);
+ if (record) {
json_parser_load_from_data (parser, record, -1, NULL);
json = json_node_get_object (json_parser_get_root (parser));
@@ -980,13 +965,14 @@ obtain_sync_key_bundles (SoupSession *session,
}
g_free (record);
+ } else {
+ /* TODO: Notify the user that the sync failed due to missing sync keys. */
+ g_warning ("Failed to retrieve the sync key bundles");
}
- g_free (payload);
g_free (kB);
- g_free (aes_key);
- g_free (hmac_key);
g_object_unref (parser);
+ ephy_sync_crypto_key_bundle_free (bundle);
out:
ephy_sync_service_send_next_storage_request (service);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]