[tor-commits] [tor/master] hs-v3: Decrypt the descriptor with client private key

nickm at torproject.org nickm at torproject.org
Fri Sep 7 19:06:18 UTC 2018


commit 7acb720027dd702332fb5a539683742801443f00
Author: Suphanat Chunhapanya <haxx.pop at gmail.com>
Date:   Fri Apr 20 02:10:19 2018 +0700

    hs-v3: Decrypt the descriptor with client private key
    
    Parse the client authorization section from the descriptor, use the client
    private key to decrypt the auth clients, and then use the descriptor cookie to
    decrypt the descriptor.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/feature/hs/hs_descriptor.c | 142 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 137 insertions(+), 5 deletions(-)

diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index bb2cc1984..3f9b505cb 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -1181,6 +1181,42 @@ desc_encode_v3(const hs_descriptor_t *desc,
 
 /* === DECODING === */
 
+/* Given the token tok for an auth client, decode it as
+ * hs_desc_authorized_client_t. tok->args MUST contain at least 3 elements
+ * Return 0 on success else -1 on failure. */
+static int
+decode_auth_client(const directory_token_t *tok,
+                   hs_desc_authorized_client_t *client)
+{
+  int ret = -1;
+
+  tor_assert(tok);
+  tor_assert(tok->n_args >= 3);
+  tor_assert(client);
+
+  if (base64_decode((char *) client->client_id, sizeof(client->client_id),
+                    tok->args[0], strlen(tok->args[0])) !=
+      sizeof(client->client_id)) {
+    goto done;
+  }
+  if (base64_decode((char *) client->iv, sizeof(client->iv),
+                    tok->args[1], strlen(tok->args[1])) !=
+      sizeof(client->iv)) {
+    goto done;
+  }
+  if (base64_decode((char *) client->encrypted_cookie,
+                    sizeof(client->encrypted_cookie),
+                    tok->args[2], strlen(tok->args[2])) !=
+      sizeof(client->encrypted_cookie)) {
+    goto done;
+  }
+
+  /* Success. */
+  ret = 0;
+ done:
+  return ret;
+}
+
 /* Given an encoded string of the link specifiers, return a newly allocated
  * list of decoded link specifiers. Return NULL on error. */
 STATIC smartlist_t *
@@ -1420,6 +1456,73 @@ encrypted_data_length_is_valid(size_t len)
   return 0;
 }
 
+/* Decrypt the descriptor cookie given the descriptor, the auth client,
+ * and the client secret key. On sucess, return 0 and a newly allocated
+ * descriptor cookie descriptor_cookie_out. On error or if the client id
+ * is invalid, return -1 and descriptor_cookie_out is set to
+ * NULL. */
+static int
+decrypt_descriptor_cookie(const hs_descriptor_t *desc,
+                          const hs_desc_authorized_client_t *client,
+                          const curve25519_secret_key_t *client_sk,
+                          uint8_t **descriptor_cookie_out)
+{
+  int ret = -1;
+  uint8_t secret_seed[CURVE25519_OUTPUT_LEN];
+  uint8_t keystream[HS_DESC_CLIENT_ID_LEN + HS_DESC_COOKIE_KEY_LEN];
+  uint8_t *cookie_key = NULL;
+  uint8_t *descriptor_cookie = NULL;
+  crypto_cipher_t *cipher = NULL;
+  crypto_xof_t *xof = NULL;
+
+  tor_assert(desc);
+  tor_assert(client);
+  tor_assert(client_sk);
+  tor_assert(!tor_mem_is_zero(
+        (char *) &desc->superencrypted_data.auth_ephemeral_pubkey,
+        sizeof(desc->superencrypted_data.auth_ephemeral_pubkey)));
+  tor_assert(!tor_mem_is_zero((char *) client_sk,
+                              sizeof(*client_sk)));
+
+  /* Calculate x25519(client_x, hs_Y) */
+  curve25519_handshake(secret_seed, client_sk,
+                       &desc->superencrypted_data.auth_ephemeral_pubkey);
+
+  /* Calculate KEYS = KDF(SECRET_SEED, 40) */
+  xof = crypto_xof_new();
+  crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed));
+  crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream));
+  crypto_xof_free(xof);
+
+  /* If the client id of auth client is not the same as the calculcated
+   * client id, it means that this auth client is invaild according to the
+   * client secret key client_sk. */
+  if (tor_memneq(client->client_id, keystream, HS_DESC_CLIENT_ID_LEN)) {
+    goto done;
+  }
+  cookie_key = keystream + HS_DESC_CLIENT_ID_LEN;
+
+  /* This creates a cipher for AES. It can't fail. */
+  cipher = crypto_cipher_new_with_iv_and_bits(cookie_key, client->iv,
+                                              HS_DESC_COOKIE_KEY_BIT_SIZE);
+  descriptor_cookie = tor_malloc_zero(HS_DESC_DESCRIPTOR_COOKIE_LEN);
+  /* This can't fail. */
+  crypto_cipher_decrypt(cipher, (char *) descriptor_cookie,
+                        (const char *) client->encrypted_cookie,
+                        sizeof(client->encrypted_cookie));
+
+  /* Success. */
+  ret = 0;
+ done:
+  *descriptor_cookie_out = descriptor_cookie;
+  if (cipher) {
+    crypto_cipher_free(cipher);
+  }
+  memwipe(secret_seed, 0, sizeof(secret_seed));
+  memwipe(keystream, 0, sizeof(keystream));
+  return ret;
+}
+
 /** Decrypt an encrypted descriptor layer at <b>encrypted_blob</b> of size
  *  <b>encrypted_blob_size</b>. The descriptor cookie is optional. Use
  *  the descriptor object <b>desc</b> and <b>descriptor_cookie</b>
@@ -1588,18 +1691,30 @@ desc_decrypt_encrypted(const hs_descriptor_t *desc,
 {
   size_t encrypted_len = 0;
   char *encrypted_plaintext = NULL;
+  uint8_t *descriptor_cookie = NULL;
 
   tor_assert(desc);
+  tor_assert(desc->superencrypted_data.clients);
   tor_assert(decrypted_out);
 
-  /* XXX: We need to decrypt the descriptor properly when the client auth
-   * is enabled. */
-  (void) client_sk;
+  /* If the client secret key is provided, try to find a valid descriptor
+   * cookie. Otherwise, leave it NULL. */
+  if (client_sk) {
+    SMARTLIST_FOREACH_BEGIN(desc->superencrypted_data.clients,
+                            hs_desc_authorized_client_t *, client) {
+      /* If we can decrypt the descriptor cookie successfully, we will use that
+       * descriptor cookie and break from the loop. */
+      if (!decrypt_descriptor_cookie(desc, client, client_sk,
+                                     &descriptor_cookie)) {
+        break;
+      }
+    } SMARTLIST_FOREACH_END(client);
+  }
 
   encrypted_len = decrypt_desc_layer(desc,
                                  desc->superencrypted_data.encrypted_blob,
                                  desc->superencrypted_data.encrypted_blob_size,
-                                 NULL, 0, &encrypted_plaintext);
+                                 descriptor_cookie, 0, &encrypted_plaintext);
   if (!encrypted_len) {
     log_warn(LD_REND, "Decrypting encrypted desc failed.");
     goto err;
@@ -1610,6 +1725,10 @@ desc_decrypt_encrypted(const hs_descriptor_t *desc,
   /* In case of error, encrypted_plaintext is already NULL, so the
    * following line makes sense. */
   *decrypted_out = encrypted_plaintext;
+  if (descriptor_cookie) {
+    memwipe(descriptor_cookie, 0, HS_DESC_DESCRIPTOR_COOKIE_LEN);
+  }
+  tor_free(descriptor_cookie);
   /* This makes sense too, because, in case of error, this is zero. */
   return encrypted_len;
 }
@@ -2125,10 +2244,23 @@ desc_decode_superencrypted_v3(const hs_descriptor_t *desc,
   }
 
   /* Extract desc auth client items */
+  if (!superencrypted->clients) {
+    superencrypted->clients = smartlist_new();
+  }
   SMARTLIST_FOREACH_BEGIN(tokens, const directory_token_t *, token) {
     if (token->tp == R3_DESC_AUTH_CLIENT) {
       tor_assert(token->n_args >= 3);
-      /* XXX: Extract each auth client. */
+
+      hs_desc_authorized_client_t *client =
+        tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
+
+      if (decode_auth_client(token, client) < 0) {
+        log_warn(LD_REND, "Descriptor client authorization section can't "
+                          "be decoded.");
+        tor_free(client);
+        goto err;
+      }
+      smartlist_add(superencrypted->clients, client);
     }
   } SMARTLIST_FOREACH_END(token);
 





More information about the tor-commits mailing list