[epiphany/wip/sync-rebase: 68/74] ephy-sync: Add/improve comments
- From: Gabriel - Cristian Ivascu <gabrielivascu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/wip/sync-rebase: 68/74] ephy-sync: Add/improve comments
- Date: Thu, 29 Sep 2016 17:48:31 +0000 (UTC)
commit 0e0650721087fdee7fb09ceee53b62b24be28fef
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date: Thu Sep 1 13:52:46 2016 +0300
ephy-sync: Add/improve comments
src/bookmarks/ephy-bookmark.c | 19 +++++++++++++++++
src/ephy-sync-crypto.c | 45 +++++++++++++++++++++++++++++++++++-----
src/ephy-sync-service.c | 30 +++++++++++++++++----------
3 files changed, 77 insertions(+), 17 deletions(-)
---
diff --git a/src/bookmarks/ephy-bookmark.c b/src/bookmarks/ephy-bookmark.c
index e81955c..74cebed 100644
--- a/src/bookmarks/ephy-bookmark.c
+++ b/src/bookmarks/ephy-bookmark.c
@@ -508,6 +508,18 @@ ephy_bookmark_to_bso (EphyBookmark *self)
g_return_val_if_fail (EPHY_IS_BOOKMARK (self), NULL);
+ /* Convert a Bookmark object to a BSO (Basic Store Object). That is a generic
+ * JSON wrapper around all items passed into and out of the SyncStorage server.
+ * The current flow is:
+ * 1. Serialize the Bookmark to a JSON string.
+ * 2. Encrypt the JSON string using the sync key from the sync service.
+ * 3. Encode the encrypted bytes to base64 url safe.
+ * 4. Create a new JSON string that contains the id of the Bookmark and the
+ encoded bytes as payload. This is actually the BSO that is going to be
+ stored on the SyncStorage server.
+ * See https://docs.services.mozilla.com/storage/apis-1.5.html
+ */
+
service = ephy_shell_get_sync_service (ephy_shell_get_default ());
sync_key = ephy_sync_crypto_decode_hex (ephy_sync_service_get_token (service, TOKEN_KB));
serialized = json_gobject_to_data (G_OBJECT (self), NULL);
@@ -538,6 +550,13 @@ ephy_bookmark_from_bso (JsonObject *bso)
g_return_val_if_fail (bso != NULL, NULL);
+ /* Convert a BSO to a Bookmark object. The flow is similar to the one from
+ * ephy_bookmark_to_bso(), only that the steps are reversed:
+ * 1. Decode the payload from base64 url safe to raw bytes.
+ * 2. Decrypt the bytes using the sync key to obtain the serialized Bookmark.
+ * 3. Deserialize the JSON string into a Bookmark object.
+ */
+
service = ephy_shell_get_sync_service (ephy_shell_get_default ());
sync_key = ephy_sync_crypto_decode_hex (ephy_sync_service_get_token (service, TOKEN_KB));
decoded = ephy_sync_crypto_base64_urlsafe_decode (json_object_get_string_member (bso, "payload"),
diff --git a/src/ephy-sync-crypto.c b/src/ephy-sync-crypto.c
index 3e43796..8cdeb42 100644
--- a/src/ephy-sync-crypto.c
+++ b/src/ephy-sync-crypto.c
@@ -178,6 +178,9 @@ ephy_sync_crypto_kw (const char *name)
{
g_assert (name != NULL);
+ /* Concatenate the given name to the Mozilla prefix.
+ * See https://raw.githubusercontent.com/wiki/mozilla/fxa-auth-server/images/onepw-create.png
+ */
return g_strconcat ("identity.mozilla.com/picl/v1/", name, NULL);
}
@@ -320,6 +323,7 @@ ephy_sync_crypto_calculate_mac (const char *type,
g_assert (key != NULL);
g_assert (artifacts != NULL);
+ /* Serialize the mac type and artifacts into a HAWK string. */
normalized = ephy_sync_crypto_normalize_string (type, artifacts);
digest_hex = g_compute_hmac_for_string (G_CHECKSUM_SHA256, key, key_len, normalized, -1);
digest = ephy_sync_crypto_decode_hex (digest_hex);
@@ -379,7 +383,10 @@ ephy_sync_crypto_hkdf (guint8 *in,
hash_len = g_checksum_type_get_length (G_CHECKSUM_SHA256);
g_assert (out_len <= hash_len * 255);
- /* If salt value was not provided, use an array of hash_len zeros */
+ /* Implementation of the HMAC-based Extract-and-Expand Key Derivation Function.
+ * See https://tools.ietf.org/html/rfc5869 */
+
+ /* If salt value was not provided, use an array of hash_len zeros. */
if (salt == NULL) {
salt = g_malloc0 (hash_len);
salt_len = hash_len;
@@ -468,6 +475,7 @@ ephy_sync_crypto_process_key_fetch_token (const char *keyFetchToken,
out1 = g_malloc (3 * EPHY_SYNC_TOKEN_LENGTH);
out2 = g_malloc (3 * EPHY_SYNC_TOKEN_LENGTH);
+ /* Use the keyFetchToken to derive tokenID, reqHMACkey and keyRequestKey. */
ephy_sync_crypto_hkdf (kft, EPHY_SYNC_TOKEN_LENGTH,
NULL, 0,
(guint8 *)info_kft, strlen (info_kft),
@@ -480,6 +488,7 @@ ephy_sync_crypto_process_key_fetch_token (const char *keyFetchToken,
memcpy (*reqHMACkey, out1 + EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
memcpy (keyRequestKey, out1 + 2 * EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
+ /* Use the keyRequestKey to derive respHMACkey and respXORkey. */
ephy_sync_crypto_hkdf (keyRequestKey, EPHY_SYNC_TOKEN_LENGTH,
NULL, 0,
(guint8 *)info_keys, strlen (info_keys),
@@ -517,6 +526,7 @@ ephy_sync_crypto_process_session_token (const char *sessionToken,
info = ephy_sync_crypto_kw ("sessionToken");
out = g_malloc (3 * EPHY_SYNC_TOKEN_LENGTH);
+ /* Use the sessionToken to derive tokenID, reqHMACkey and requestKey. */
ephy_sync_crypto_hkdf (st, EPHY_SYNC_TOKEN_LENGTH,
NULL, 0,
(guint8 *)info, strlen (info),
@@ -563,6 +573,7 @@ ephy_sync_crypto_compute_sync_keys (const char *bundle,
wrapKB = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
*kA = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+ /* Compute the MAC and compare it to the expected value. */
memcpy (ciphertext, bdl, 2 * EPHY_SYNC_TOKEN_LENGTH);
memcpy (respMAC, bdl + 2 * EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
respMAC2_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256,
@@ -571,9 +582,13 @@ ephy_sync_crypto_compute_sync_keys (const char *bundle,
respMAC2 = ephy_sync_crypto_decode_hex (respMAC2_hex);
g_assert (ephy_sync_crypto_equals (respMAC, respMAC2, EPHY_SYNC_TOKEN_LENGTH) == TRUE);
+ /* XOR the extracted ciphertext with the respXORkey, then split in into the
+ * separate kA and wrap(kB) values. */
xored = ephy_sync_crypto_xor (ciphertext, respXORkey, 2 * EPHY_SYNC_TOKEN_LENGTH);
memcpy (*kA, xored, EPHY_SYNC_TOKEN_LENGTH);
memcpy (wrapKB, xored + EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
+
+ /* Finally, XOR wrap(kB) with unwrapBKey to obtain kB. There is no MAC on wrap(kB). */
*kB = ephy_sync_crypto_xor (unwrapBKey, wrapKB, EPHY_SYNC_TOKEN_LENGTH);
g_free (bdl);
@@ -637,9 +652,12 @@ ephy_sync_crypto_compute_hawk_header (const char *url,
if (hash == NULL && payload != NULL) {
const char *content_type = options ? options->content_type : "text/plain";
+
+ /* Calculate the hash for the given payload. */
hash = ephy_sync_crypto_calculate_payload_hash (payload, content_type);
}
+ /* Create the artifacts from the options. */
artifacts = ephy_sync_crypto_hawk_artifacts_new (options ? options->app : NULL,
options ? options->dlg : NULL,
options ? options->ext : NULL,
@@ -651,16 +669,16 @@ ephy_sync_crypto_compute_hawk_header (const char *url,
resource,
ts);
- mac = ephy_sync_crypto_calculate_mac ("header", key, key_len, artifacts);
-
header = g_strconcat ("Hawk id=\"", id, "\"",
", ts=\"", artifacts->ts, "\"",
", nonce=\"", artifacts->nonce, "\"",
NULL);
+ /* Append pre-calculated payload hash if any. */
if (artifacts->hash != NULL && strlen (artifacts->hash) > 0)
header = ephy_sync_crypto_append_to_header (header, "hash", artifacts->hash);
+ /* Append the application specific data if any. */
if (artifacts->ext != NULL && strlen (artifacts->ext) > 0) {
char *h_ext;
char *tmp_ext;
@@ -673,11 +691,15 @@ ephy_sync_crypto_compute_hawk_header (const char *url,
g_free (tmp_ext);
}
+ /* Calculate and append a message authentication code (MAC). */
+ mac = ephy_sync_crypto_calculate_mac ("header", key, key_len, artifacts);
header = ephy_sync_crypto_append_to_header (header, "mac", mac);
+ /* Append the Oz application id if any. */
if (artifacts->app != NULL) {
header = ephy_sync_crypto_append_to_header (header, "app", artifacts->app);
+ /* Append the Oz delegated-by application id if any. */
if (artifacts->dlg != NULL)
header = ephy_sync_crypto_append_to_header (header, "dlg", artifacts->dlg);
}
@@ -703,10 +725,10 @@ ephy_sync_crypto_generate_rsa_key_pair (void)
rsa_public_key_init (&public);
rsa_private_key_init (&private);
- /* The public exponent, usually one of the small Fermat primes 3, 5, 17, 257, 65537 */
+ /* The public exponent, usually one of the small Fermat primes 3, 5, 17, 257, 65537. */
mpz_set_ui (public.e, 65537);
- /* Key sizes below 2048 are considered breakable and should not be used */
+ /* Key sizes below 2048 are considered breakable and should not be used. */
retval = rsa_generate_keypair (&public, &private,
NULL, ephy_sync_crypto_random_hex_gen,
NULL, NULL, 2048, 0);
@@ -745,16 +767,19 @@ ephy_sync_crypto_create_assertion (const char *certificate,
g_return_val_if_fail (audience != NULL, NULL);
g_return_val_if_fail (keypair != NULL, NULL);
+ /* Encode the header and body to base64 url safe and join them. */
expires_at = g_get_real_time () / 1000 + duration * 1000;
body = g_strdup_printf ("{\"exp\": %lu, \"aud\": \"%s\"}", expires_at, audience);
body_b64 = ephy_sync_crypto_base64_urlsafe_encode ((guint8 *)body, strlen (body), TRUE);
header_b64 = ephy_sync_crypto_base64_urlsafe_encode ((guint8 *)header, strlen (header), TRUE);
to_sign = g_strdup_printf ("%s.%s", header_b64, body_b64);
- mpz_init (signature);
+ /* Compute the SHA256 hash of the message to be signed. */
digest_hex = g_compute_checksum_for_string (G_CHECKSUM_SHA256, to_sign, -1);
digest = ephy_sync_crypto_decode_hex (digest_hex);
+ /* Use the provided key pair to RSA sign the message. */
+ mpz_init (signature);
if (rsa_sha256_sign_digest_tr (&keypair->public, &keypair->private,
NULL, ephy_sync_crypto_random_hex_gen,
digest, signature) == 0) {
@@ -771,6 +796,7 @@ ephy_sync_crypto_create_assertion (const char *certificate,
goto out;
}
+ /* Finally, join certificate, header, body and signed message to create the assertion. */
sig_b64 = ephy_sync_crypto_base64_urlsafe_encode (sig, count, TRUE);
assertion = g_strdup_printf ("%s~%s.%s.%s", certificate, header_b64, body_b64, sig_b64);
@@ -829,6 +855,7 @@ ephy_sync_crypto_base64_urlsafe_encode (guint8 *data,
base64 = g_base64_encode (data, data_len);
end = strlen (base64) - 1;
+ /* Strip the data of any leading or trailing '=' characters. */
if (strip == TRUE) {
while (start < strlen (base64) && base64[start] == '=')
start++;
@@ -857,6 +884,7 @@ ephy_sync_crypto_base64_urlsafe_decode (const char *text,
g_return_val_if_fail (text != NULL, NULL);
g_return_val_if_fail (out_len != NULL, NULL);
+ /* Fill the text with trailing '=' characters up to the proper length. */
if (fill == TRUE)
suffix = g_strnfill ((4 - strlen (text) % 4) % 4, '=');
@@ -885,6 +913,11 @@ ephy_sync_crypto_aes_256 (EphySyncCryptoAES256Mode mode,
g_return_val_if_fail (key != NULL, NULL);
g_return_val_if_fail (data != NULL, NULL);
+ /* Since Nettle enforces the length of the data to be a multiple of
+ * AES_BLOCK_SIZE, the data needs to be padded accordingly. Because any
+ * data that is decrypted has to be encrypted first, crash if the length
+ * is incorrect at decryption.
+ */
if (mode == AES_256_MODE_ENCRYPT)
padded_len = data_len + (AES_BLOCK_SIZE - data_len % AES_BLOCK_SIZE);
else if (mode == AES_256_MODE_DECRYPT)
diff --git a/src/ephy-sync-service.c b/src/ephy-sync-service.c
index 1140fdb..2d7824d 100644
--- a/src/ephy-sync-service.c
+++ b/src/ephy-sync-service.c
@@ -308,6 +308,9 @@ ephy_sync_service_certificate_is_valid (EphySyncService *self,
g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), FALSE);
g_return_val_if_fail (certificate != NULL, FALSE);
+ /* Check if the certificate is something that we were expecting, i.e.
+ * if the algorithm and email fields match the expected values. */
+
uri = soup_uri_new (MOZILLA_FXA_SERVER_URL);
pieces = g_strsplit (certificate, ".", 0);
header = (char *)ephy_sync_crypto_base64_urlsafe_decode (pieces[0], &len, TRUE);
@@ -420,8 +423,7 @@ ephy_sync_service_obtain_storage_credentials (EphySyncService *self,
msg = soup_message_new (SOUP_METHOD_GET, MOZILLA_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 (msg->request_headers, "X-Client-State", client_state);
soup_message_headers_append (msg->request_headers, "authorization", authorization);
soup_session_queue_message (self->session, msg, obtain_storage_credentials_response_cb, user_data);
@@ -508,12 +510,14 @@ ephy_sync_service_obtain_signed_certificate (EphySyncService *self,
g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
g_return_if_fail (self->sessionToken != NULL);
+ /* Generate a new RSA key pair that is going to be used to sign the new certificate. */
if (self->keypair != NULL)
ephy_sync_crypto_rsa_key_pair_free (self->keypair);
self->keypair = ephy_sync_crypto_generate_rsa_key_pair ();
g_return_if_fail (self->keypair != NULL);
+ /* Derive tokenID, reqHMACkey and requestKey from the sessionToken. */
ephy_sync_crypto_process_session_token (self->sessionToken, &tokenID, &reqHMACkey, &requestKey);
tokenID_hex = ephy_sync_crypto_encode_hex (tokenID, 0);
@@ -522,8 +526,7 @@ ephy_sync_service_obtain_signed_certificate (EphySyncService *self,
public_key_json = ephy_sync_utils_build_json_string ("algorithm", "RS", "n", n, "e", e, NULL);
/* Duration is the lifetime of the certificate in milliseconds. The FxA server
* limits the duration to 24 hours. For our purposes, a duration of 30 minutes
- * will suffice.
- */
+ * will suffice. */
request_body = g_strdup_printf ("{\"publicKey\": %s, \"duration\": %d}",
public_key_json, 30 * 60 * 1000);
ephy_sync_service_fxa_hawk_post_async (self, "certificate/sign", tokenID_hex,
@@ -556,8 +559,7 @@ ephy_sync_service_issue_storage_request (EphySyncService *self,
* ephy_sync_service_obtain_signed_certificate() and
* ephy_sync_service_obtain_storage_credentials() complete asynchronously,
* we need to entrust them the task of sending the request to the Storage
- * Server.
- */
+ * Server. */
ephy_sync_service_obtain_signed_certificate (self, data);
} else {
ephy_sync_service_send_storage_request (self, data);
@@ -859,6 +861,12 @@ ephy_sync_service_fetch_sync_keys (EphySyncService *self,
g_return_val_if_fail (unwrapBKey != NULL, FALSE);
unwrapKB = ephy_sync_crypto_decode_hex (unwrapBKey);
+
+ /* Derive tokenID, reqHMACkey, respHMACkey and respXORkey from the keyFetchToken.
+ * tokenID and reqHMACkey are used to make a HAWK request to the "GET /account/keys"
+ * API. The server looks up the stored table entry with tokenID, checks the request
+ * HMAC for validity, then returns the pre-encrypted response.
+ * See https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol#fetching-sync-keys */
ephy_sync_crypto_process_key_fetch_token (keyFetchToken,
&tokenID, &reqHMACkey, &respHMACkey, &respXORkey);
tokenID_hex = ephy_sync_crypto_encode_hex (tokenID, 0);
@@ -874,6 +882,8 @@ ephy_sync_service_fetch_sync_keys (EphySyncService *self,
goto out;
}
+ /* From the pre-encrypted response and respHMACkey, respXORkey, unwrapKB
+ * derive the sync keys. */
ephy_sync_crypto_compute_sync_keys (json_object_get_string_member (json, "bundle"),
respHMACkey, respXORkey, unwrapKB,
&kA, &kB);
@@ -923,8 +933,7 @@ ephy_sync_service_send_storage_message (EphySyncService *self,
callback, user_data);
/* If there is currently another message being transmitted, then the new
- * message has to wait in the queue, otherwise, it is free to go.
- */
+ * message has to wait in the queue, otherwise, it is free to go. */
if (self->locked == FALSE) {
self->locked = TRUE;
ephy_sync_service_issue_storage_request (self, data);
@@ -941,8 +950,7 @@ ephy_sync_service_release_next_storage_message (EphySyncService *self)
g_assert (self->locked == TRUE);
/* If there are other messages waiting in the queue, we release the next one
- * and keep the service locked, else, we mark the service as not locked.
- */
+ * and keep the service locked, else, we mark the service as not locked. */
if (g_queue_is_empty (self->storage_queue) == FALSE)
ephy_sync_service_issue_storage_request (self, g_queue_pop_head (self->storage_queue));
else
@@ -1282,7 +1290,7 @@ sync_bookmarks_response_cb (SoupSession *session,
parser = json_parser_new ();
json_parser_load_from_data (parser, msg->response_body->data, -1, NULL);
- /* Code 304 indicates that the resource has not been modifiedf. Therefore,
+ /* Code 304 indicates that the resource has not been modified. Therefore,
* only upload the local bookmarks that were not uploaded. */
if (msg->status_code == 304)
goto handle_local_bookmarks;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]