[tor-commits] [tor/master] hs-v3: Refactor the descriptor decryption/decoding

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


commit 63576b01663f1af0ee2b7bd29dd840d121103315
Author: Suphanat Chunhapanya <haxx.pop at gmail.com>
Date:   Thu Apr 19 22:44:17 2018 +0700

    hs-v3: Refactor the descriptor decryption/decoding
    
    This commit refactors the existing decryption code to make it compatible with
    a new logic for when the client authorization is enabled.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/feature/hs/hs_client.c     |  23 ++-
 src/feature/hs/hs_descriptor.c | 402 +++++++++++++++++++++++++----------------
 src/feature/hs/hs_descriptor.h |   7 +-
 src/test/fuzz/fuzz_hsdescv3.c  |   4 +-
 src/test/test_hs_cache.c       |   4 +-
 src/test/test_hs_descriptor.c  | 107 +----------
 6 files changed, 279 insertions(+), 268 deletions(-)

diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 18c79e0c4..0038fdfa5 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -1181,6 +1181,19 @@ can_client_refetch_desc(const ed25519_public_key_t *identity_pk,
   return 0;
 }
 
+/* Return the client auth in the map using the service identity public key.
+ * Return NULL if it does not exist in the map. */
+static hs_client_service_authorization_t *
+find_client_auth(const ed25519_public_key_t *service_identity_pk)
+{
+  /* If the map is not allocated, we can assume that we do not have any client
+   * auth information. */
+  if (!client_auths) {
+    return NULL;
+  }
+  return digest256map_get(client_auths, service_identity_pk->pubkey);
+}
+
 /* ========== */
 /* Public API */
 /* ========== */
@@ -1219,11 +1232,19 @@ hs_client_decode_descriptor(const char *desc_str,
   int ret;
   uint8_t subcredential[DIGEST256_LEN];
   ed25519_public_key_t blinded_pubkey;
+  hs_client_service_authorization_t *client_auth = NULL;
+  curve25519_secret_key_t *client_sk = NULL;
 
   tor_assert(desc_str);
   tor_assert(service_identity_pk);
   tor_assert(desc);
 
+  /* Check if we have a client authorization for this service in the map. */
+  client_auth = find_client_auth(service_identity_pk);
+  if (client_auth) {
+    client_sk = &client_auth->enc_seckey;
+  }
+
   /* Create subcredential for this HS so that we can decrypt */
   {
     uint64_t current_time_period = hs_get_time_period_num(0);
@@ -1233,7 +1254,7 @@ hs_client_decode_descriptor(const char *desc_str,
   }
 
   /* Parse descriptor */
-  ret = hs_desc_decode_descriptor(desc_str, subcredential, desc);
+  ret = hs_desc_decode_descriptor(desc_str, subcredential, client_sk, desc);
   memwipe(subcredential, 0, sizeof(subcredential));
   if (ret < 0) {
     log_warn(LD_GENERAL, "Could not parse received descriptor as client.");
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 4eb06c827..bb2cc1984 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -1421,10 +1421,11 @@ encrypted_data_length_is_valid(size_t len)
 }
 
 /** Decrypt an encrypted descriptor layer at <b>encrypted_blob</b> of size
- *  <b>encrypted_blob_size</b>. Use the descriptor object <b>desc</b> to
- *  generate the right decryption keys; set <b>decrypted_out</b> to the
- *  plaintext. If <b>is_superencrypted_layer</b> is set, this is the outter
- *  encrypted layer of the descriptor.
+ *  <b>encrypted_blob_size</b>. The descriptor cookie is optional. Use
+ *  the descriptor object <b>desc</b> and <b>descriptor_cookie</b>
+ *  to generate the right decryption keys; set <b>decrypted_out</b> to
+ *  the plaintext. If <b>is_superencrypted_layer</b> is set, this is
+ *  the outter encrypted layer of the descriptor.
  *
  * On any error case, including an empty output, return 0 and set
  * *<b>decrypted_out</b> to NULL.
@@ -1433,11 +1434,14 @@ MOCK_IMPL(STATIC size_t,
 decrypt_desc_layer,(const hs_descriptor_t *desc,
                     const uint8_t *encrypted_blob,
                     size_t encrypted_blob_size,
+                    const uint8_t *descriptor_cookie,
                     int is_superencrypted_layer,
                     char **decrypted_out))
 {
   uint8_t *decrypted = NULL;
   uint8_t secret_key[HS_DESC_ENCRYPTED_KEY_LEN], secret_iv[CIPHER_IV_LEN];
+  uint8_t *secret_data = NULL;
+  size_t secret_data_len = 0;
   uint8_t mac_key[DIGEST256_LEN], our_mac[DIGEST256_LEN];
   const uint8_t *salt, *encrypted, *desc_mac;
   size_t encrypted_len, result_len = 0;
@@ -1464,13 +1468,14 @@ decrypt_desc_layer,(const hs_descriptor_t *desc,
   /* And last comes the MAC. */
   desc_mac = encrypted_blob + encrypted_blob_size - DIGEST256_LEN;
 
+  /* Build secret data to be used in the decryption. */
+  secret_data_len = build_secret_data(&desc->plaintext_data.blinded_pubkey,
+                                      descriptor_cookie,
+                                      &secret_data);
+
   /* KDF construction resulting in a key from which the secret key, IV and MAC
    * key are extracted which is what we need for the decryption. */
-  /* XXX: I will put only blinded pubkey for now. I will also put the
-   * descriptor cookie when I implement the descriptor decryption with
-   * client auth. */
-  build_secret_key_iv_mac(desc, desc->plaintext_data.blinded_pubkey.pubkey,
-                          ED25519_PUBKEY_LEN,
+  build_secret_key_iv_mac(desc, secret_data, secret_data_len,
                           salt, HS_DESC_ENCRYPTED_SALT_LEN,
                           secret_key, sizeof(secret_key),
                           secret_iv, sizeof(secret_iv),
@@ -1531,167 +1536,82 @@ decrypt_desc_layer,(const hs_descriptor_t *desc,
   result_len = 0;
 
  done:
+  memwipe(secret_data, 0, secret_data_len);
   memwipe(secret_key, 0, sizeof(secret_key));
   memwipe(secret_iv, 0, sizeof(secret_iv));
+  tor_free(secret_data);
   return result_len;
 }
 
-/* Basic validation that the superencrypted client auth portion of the
- * descriptor is well-formed and recognized. Return True if so, otherwise
- * return False. */
-static int
-superencrypted_auth_data_is_valid(smartlist_t *tokens)
-{
-  /* XXX: This is just basic validation for now. When we implement client auth,
-     we can refactor this function so that it actually parses and saves the
-     data. */
-
-  { /* verify desc auth type */
-    const directory_token_t *tok;
-    tok = find_by_keyword(tokens, R3_DESC_AUTH_TYPE);
-    tor_assert(tok->n_args >= 1);
-    if (strcmp(tok->args[0], "x25519")) {
-      log_warn(LD_DIR, "Unrecognized desc auth type");
-      return 0;
-    }
-  }
-
-  { /* verify desc auth key */
-    const directory_token_t *tok;
-    curve25519_public_key_t k;
-    tok = find_by_keyword(tokens, R3_DESC_AUTH_KEY);
-    tor_assert(tok->n_args >= 1);
-    if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
-      log_warn(LD_DIR, "Bogus desc auth key in HS desc");
-      return 0;
-    }
-  }
-
-  /* verify desc auth client items */
-  SMARTLIST_FOREACH_BEGIN(tokens, const directory_token_t *, tok) {
-    if (tok->tp == R3_DESC_AUTH_CLIENT) {
-      tor_assert(tok->n_args >= 3);
-    }
-  } SMARTLIST_FOREACH_END(tok);
-
-  return 1;
-}
-
-/* Parse <b>message</b>, the plaintext of the superencrypted portion of an HS
- * descriptor. Set <b>encrypted_out</b> to the encrypted blob, and return its
- * size */
-STATIC size_t
-decode_superencrypted(const char *message, size_t message_len,
-                     uint8_t **encrypted_out)
-{
-  int retval = 0;
-  memarea_t *area = NULL;
-  smartlist_t *tokens = NULL;
-
-  area = memarea_new();
-  tokens = smartlist_new();
-  if (tokenize_string(area, message, message + message_len, tokens,
-                      hs_desc_superencrypted_v3_token_table, 0) < 0) {
-    log_warn(LD_REND, "Superencrypted portion is not parseable");
-    goto err;
-  }
-
-  /* Do some rudimentary validation of the authentication data */
-  if (!superencrypted_auth_data_is_valid(tokens)) {
-    log_warn(LD_REND, "Invalid auth data");
-    goto err;
-  }
-
-  /* Extract the encrypted data section. */
-  {
-    const directory_token_t *tok;
-    tok = find_by_keyword(tokens, R3_ENCRYPTED);
-    tor_assert(tok->object_body);
-    if (strcmp(tok->object_type, "MESSAGE") != 0) {
-      log_warn(LD_REND, "Desc superencrypted data section is invalid");
-      goto err;
-    }
-    /* Make sure the length of the encrypted blob is valid. */
-    if (!encrypted_data_length_is_valid(tok->object_size)) {
-      goto err;
-    }
-
-    /* Copy the encrypted blob to the descriptor object so we can handle it
-     * latter if needed. */
-    tor_assert(tok->object_size <= INT_MAX);
-    *encrypted_out = tor_memdup(tok->object_body, tok->object_size);
-    retval = (int) tok->object_size;
-  }
-
- err:
-  SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
-  smartlist_free(tokens);
-  if (area) {
-    memarea_drop_all(area);
-  }
-
-  return retval;
-}
-
-/* Decrypt both the superencrypted and the encrypted section of the descriptor
- * using the given descriptor object <b>desc</b>. A newly allocated NUL
- * terminated string is put in decrypted_out which contains the inner encrypted
- * layer of the descriptor. Return the length of decrypted_out on success else
- * 0 is returned and decrypted_out is set to NULL. */
+/* Decrypt the superencrypted section of the descriptor using the given
+ * descriptor object <b>desc</b>. A newly allocated NUL terminated string is
+ * put in decrypted_out which contains the superencrypted layer of the
+ * descriptor. Return the length of decrypted_out on success else 0 is
+ * returned and decrypted_out is set to NULL. */
 static size_t
-desc_decrypt_all(const hs_descriptor_t *desc, char **decrypted_out)
+desc_decrypt_superencrypted(const hs_descriptor_t *desc, char **decrypted_out)
 {
-  size_t  decrypted_len = 0;
-  size_t encrypted_len = 0;
   size_t superencrypted_len = 0;
   char *superencrypted_plaintext = NULL;
-  uint8_t *encrypted_blob = NULL;
 
-  /** Function logic: This function takes us from the descriptor header to the
-   *  inner encrypted layer, by decrypting and decoding the middle descriptor
-   *  layer. In the end we return the contents of the inner encrypted layer to
-   *  our caller. */
+  tor_assert(desc);
+  tor_assert(decrypted_out);
 
-  /* 1. Decrypt middle layer of descriptor */
   superencrypted_len = decrypt_desc_layer(desc,
                                  desc->plaintext_data.superencrypted_blob,
                                  desc->plaintext_data.superencrypted_blob_size,
-                                 1,
-                                 &superencrypted_plaintext);
+                                 NULL, 1, &superencrypted_plaintext);
+
   if (!superencrypted_len) {
     log_warn(LD_REND, "Decrypting superencrypted desc failed.");
-    goto err;
+    goto done;
   }
   tor_assert(superencrypted_plaintext);
 
-  /* 2. Parse "superencrypted" */
-  encrypted_len = decode_superencrypted(superencrypted_plaintext,
-                                        superencrypted_len,
-                                        &encrypted_blob);
-  if (!encrypted_len) {
-    log_warn(LD_REND, "Decrypting encrypted desc failed.");
-    goto err;
-  }
-  tor_assert(encrypted_blob);
+ done:
+  /* In case of error, superencrypted_plaintext is already NULL, so the
+   * following line makes sense. */
+  *decrypted_out = superencrypted_plaintext;
+  /* This makes sense too, because, in case of error, this is zero. */
+  return superencrypted_len;
+}
+
+/* Decrypt the encrypted section of the descriptor using the given descriptor
+ * object <b>desc</b>. A newly allocated NUL terminated string is put in
+ * decrypted_out which contains the encrypted layer of the descriptor.
+ * Return the length of decrypted_out on success else 0 is returned and
+ * decrypted_out is set to NULL. */
+static size_t
+desc_decrypt_encrypted(const hs_descriptor_t *desc,
+                       const curve25519_secret_key_t *client_sk,
+                       char **decrypted_out)
+{
+  size_t encrypted_len = 0;
+  char *encrypted_plaintext = NULL;
+
+  tor_assert(desc);
+  tor_assert(decrypted_out);
+
+  /* XXX: We need to decrypt the descriptor properly when the client auth
+   * is enabled. */
+  (void) client_sk;
 
-  /* 3. Decrypt "encrypted" and set decrypted_out */
-  char *decrypted_desc;
-  decrypted_len = decrypt_desc_layer(desc,
-                                     encrypted_blob, encrypted_len,
-                                     0, &decrypted_desc);
-  if (!decrypted_len) {
+  encrypted_len = decrypt_desc_layer(desc,
+                                 desc->superencrypted_data.encrypted_blob,
+                                 desc->superencrypted_data.encrypted_blob_size,
+                                 NULL, 0, &encrypted_plaintext);
+  if (!encrypted_len) {
     log_warn(LD_REND, "Decrypting encrypted desc failed.");
     goto err;
   }
-  tor_assert(decrypted_desc);
-
-  *decrypted_out = decrypted_desc;
+  tor_assert(encrypted_plaintext);
 
  err:
-  tor_free(superencrypted_plaintext);
-  tor_free(encrypted_blob);
-
-  return decrypted_len;
+  /* In case of error, encrypted_plaintext is already NULL, so the
+   * following line makes sense. */
+  *decrypted_out = encrypted_plaintext;
+  /* This makes sense too, because, in case of error, this is zero. */
+  return encrypted_len;
 }
 
 /* Given the token tok for an intro point legacy key, the list of tokens, the
@@ -2118,19 +2038,19 @@ desc_decode_plaintext_v3(smartlist_t *tokens,
     goto err;
   }
 
-  /* Extract the encrypted data section. */
+  /* Extract the superencrypted data section. */
   tok = find_by_keyword(tokens, R3_SUPERENCRYPTED);
   tor_assert(tok->object_body);
   if (strcmp(tok->object_type, "MESSAGE") != 0) {
-    log_warn(LD_REND, "Service descriptor encrypted data section is invalid");
+    log_warn(LD_REND, "Desc superencrypted data section is invalid");
     goto err;
   }
-  /* Make sure the length of the encrypted blob is valid. */
+  /* Make sure the length of the superencrypted blob is valid. */
   if (!encrypted_data_length_is_valid(tok->object_size)) {
     goto err;
   }
 
-  /* Copy the encrypted blob to the descriptor object so we can handle it
+  /* Copy the superencrypted blob to the descriptor object so we can handle it
    * latter if needed. */
   desc->superencrypted_blob = tor_memdup(tok->object_body, tok->object_size);
   desc->superencrypted_blob_size = tok->object_size;
@@ -2150,14 +2070,117 @@ desc_decode_plaintext_v3(smartlist_t *tokens,
   return -1;
 }
 
+/* Decode the version 3 superencrypted section of the given descriptor desc.
+ * The desc_superencrypted_out will be populated with the decoded data.
+ * Return 0 on success else -1. */
+static int
+desc_decode_superencrypted_v3(const hs_descriptor_t *desc,
+                              hs_desc_superencrypted_data_t *
+                              desc_superencrypted_out)
+{
+  int ret = -1;
+  char *message = NULL;
+  size_t message_len;
+  memarea_t *area = NULL;
+  directory_token_t *tok;
+  smartlist_t *tokens = NULL;
+  /* Rename the parameter because it is too long. */
+  hs_desc_superencrypted_data_t *superencrypted = desc_superencrypted_out;
+
+  tor_assert(desc);
+  tor_assert(desc_superencrypted_out);
+
+  /* Decrypt the superencrypted data that is located in the plaintext section
+   * in the descriptor as a blob of bytes. */
+  message_len = desc_decrypt_superencrypted(desc, &message);
+  if (!message_len) {
+    log_warn(LD_REND, "Service descriptor decryption failed.");
+    goto err;
+  }
+  tor_assert(message);
+
+  area = memarea_new();
+  tokens = smartlist_new();
+  if (tokenize_string(area, message, message + message_len,
+                      tokens, hs_desc_superencrypted_v3_token_table, 0) < 0) {
+    log_warn(LD_REND, "Superencrypted service descriptor is not parseable.");
+    goto err;
+  }
+
+  /* Verify desc auth type */
+  tok = find_by_keyword(tokens, R3_DESC_AUTH_TYPE);
+  tor_assert(tok->n_args >= 1);
+  if (strcmp(tok->args[0], "x25519")) {
+    log_warn(LD_DIR, "Unrecognized desc auth type");
+    goto err;
+  }
+
+  /* Extract desc auth ephemeral key */
+  tok = find_by_keyword(tokens, R3_DESC_AUTH_KEY);
+  tor_assert(tok->n_args >= 1);
+  if (curve25519_public_from_base64(&superencrypted->auth_ephemeral_pubkey,
+                                    tok->args[0]) < 0) {
+    log_warn(LD_DIR, "Bogus desc auth ephemeral key in HS desc");
+    goto err;
+  }
+
+  /* Extract desc auth client items */
+  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. */
+    }
+  } SMARTLIST_FOREACH_END(token);
+
+  /* Extract the encrypted data section. */
+  tok = find_by_keyword(tokens, R3_ENCRYPTED);
+  tor_assert(tok->object_body);
+  if (strcmp(tok->object_type, "MESSAGE") != 0) {
+    log_warn(LD_REND, "Desc encrypted data section is invalid");
+    goto err;
+  }
+  /* Make sure the length of the encrypted blob is valid. */
+  if (!encrypted_data_length_is_valid(tok->object_size)) {
+    goto err;
+  }
+
+  /* Copy the encrypted blob to the descriptor object so we can handle it
+   * latter if needed. */
+  tor_assert(tok->object_size <= INT_MAX);
+  superencrypted->encrypted_blob = tor_memdup(tok->object_body,
+                                              tok->object_size);
+  superencrypted->encrypted_blob_size = tok->object_size;
+
+  ret = 0;
+  goto done;
+
+ err:
+  tor_assert(ret < 0);
+  desc_superencrypted_data_free_contents(desc_superencrypted_out);
+
+ done:
+  if (tokens) {
+    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
+    smartlist_free(tokens);
+  }
+  if (area) {
+    memarea_drop_all(area);
+  }
+  if (message) {
+    tor_free(message);
+  }
+  return ret;
+}
+
 /* Decode the version 3 encrypted section of the given descriptor desc. The
  * desc_encrypted_out will be populated with the decoded data. Return 0 on
  * success else -1. */
 static int
 desc_decode_encrypted_v3(const hs_descriptor_t *desc,
+                         const curve25519_secret_key_t *client_sk,
                          hs_desc_encrypted_data_t *desc_encrypted_out)
 {
-  int result = -1;
+  int ret = -1;
   char *message = NULL;
   size_t message_len;
   memarea_t *area = NULL;
@@ -2167,9 +2190,9 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
   tor_assert(desc);
   tor_assert(desc_encrypted_out);
 
-  /* Decrypt the superencrypted data that is located in the plaintext section
+  /* Decrypt the encrypted data that is located in the superencrypted section
    * in the descriptor as a blob of bytes. */
-  message_len = desc_decrypt_all(desc, &message);
+  message_len = desc_decrypt_encrypted(desc, client_sk, &message);
   if (!message_len) {
     log_warn(LD_REND, "Service descriptor decryption failed.");
     goto err;
@@ -2228,11 +2251,11 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
   /* NOTE: Unknown fields are allowed because this function could be used to
    * decode other descriptor version. */
 
-  result = 0;
+  ret = 0;
   goto done;
 
  err:
-  tor_assert(result < 0);
+  tor_assert(ret < 0);
   desc_encrypted_data_free_contents(desc_encrypted_out);
 
  done:
@@ -2246,7 +2269,7 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
   if (message) {
     tor_free(message);
   }
-  return result;
+  return ret;
 }
 
 /* Table of encrypted decode function version specific. The function are
@@ -2254,6 +2277,7 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
 static int
   (*decode_encrypted_handlers[])(
       const hs_descriptor_t *desc,
+      const curve25519_secret_key_t *client_sk,
       hs_desc_encrypted_data_t *desc_encrypted) =
 {
   /* v0 */ NULL, /* v1 */ NULL, /* v2 */ NULL,
@@ -2265,6 +2289,7 @@ static int
  * negative value on error. */
 int
 hs_desc_decode_encrypted(const hs_descriptor_t *desc,
+                         const curve25519_secret_key_t *client_sk,
                          hs_desc_encrypted_data_t *desc_encrypted)
 {
   int ret;
@@ -2275,9 +2300,9 @@ hs_desc_decode_encrypted(const hs_descriptor_t *desc,
   version = desc->plaintext_data.version;
   tor_assert(desc_encrypted);
   /* Calling this function without an encrypted blob to parse is a code flow
-   * error. The plaintext parsing should never succeed in the first place
+   * error. The superencrypted parsing should never succeed in the first place
    * without an encrypted section. */
-  tor_assert(desc->plaintext_data.superencrypted_blob);
+  tor_assert(desc->superencrypted_data.encrypted_blob);
   /* Let's make sure we have a supported version as well. By correctly parsing
    * the plaintext, this should not fail. */
   if (BUG(!hs_desc_is_supported_version(version))) {
@@ -2290,7 +2315,58 @@ hs_desc_decode_encrypted(const hs_descriptor_t *desc,
   tor_assert(decode_encrypted_handlers[version]);
 
   /* Run the version specific plaintext decoder. */
-  ret = decode_encrypted_handlers[version](desc, desc_encrypted);
+  ret = decode_encrypted_handlers[version](desc, client_sk, desc_encrypted);
+  if (ret < 0) {
+    goto err;
+  }
+
+ err:
+  return ret;
+}
+
+/* Table of superencrypted decode function version specific. The function are
+ * indexed by the version number so v3 callback is at index 3 in the array. */
+static int
+  (*decode_superencrypted_handlers[])(
+      const hs_descriptor_t *desc,
+      hs_desc_superencrypted_data_t *desc_superencrypted) =
+{
+  /* v0 */ NULL, /* v1 */ NULL, /* v2 */ NULL,
+  desc_decode_superencrypted_v3,
+};
+
+/* Decode the superencrypted data section of the given descriptor and store the
+ * data in the given superencrypted data object. Return 0 on success else a
+ * negative value on error. */
+int
+hs_desc_decode_superencrypted(const hs_descriptor_t *desc,
+                              hs_desc_superencrypted_data_t *
+                              desc_superencrypted)
+{
+  int ret;
+  uint32_t version;
+
+  tor_assert(desc);
+  /* Ease our life a bit. */
+  version = desc->plaintext_data.version;
+  tor_assert(desc_superencrypted);
+  /* Calling this function without an superencrypted blob to parse is
+   * a code flow error. The plaintext parsing should never succeed in
+   * the first place without an superencrypted section. */
+  tor_assert(desc->plaintext_data.superencrypted_blob);
+  /* Let's make sure we have a supported version as well. By correctly parsing
+   * the plaintext, this should not fail. */
+  if (BUG(!hs_desc_is_supported_version(version))) {
+    ret = -1;
+    goto err;
+  }
+  /* Extra precaution. Having no handler for the supported version should
+   * never happened else we forgot to add it but we bumped the version. */
+  tor_assert(ARRAY_LENGTH(decode_superencrypted_handlers) >= version);
+  tor_assert(decode_superencrypted_handlers[version]);
+
+  /* Run the version specific plaintext decoder. */
+  ret = decode_superencrypted_handlers[version](desc, desc_superencrypted);
   if (ret < 0) {
     goto err;
   }
@@ -2387,12 +2463,15 @@ hs_desc_decode_plaintext(const char *encoded,
 
 /* Fully decode an encoded descriptor and set a newly allocated descriptor
  * object in desc_out. Subcredentials are used if not NULL else it's ignored.
+ * Client secret key is used to decrypt the "encrypted" section if not NULL
+ * else it's ignored.
  *
  * Return 0 on success. A negative value is returned on error and desc_out is
  * set to NULL. */
 int
 hs_desc_decode_descriptor(const char *encoded,
                           const uint8_t *subcredential,
+                          const curve25519_secret_key_t *client_sk,
                           hs_descriptor_t **desc_out)
 {
   int ret = -1;
@@ -2415,7 +2494,12 @@ hs_desc_decode_descriptor(const char *encoded,
     goto err;
   }
 
-  ret = hs_desc_decode_encrypted(desc, &desc->encrypted_data);
+  ret = hs_desc_decode_superencrypted(desc, &desc->superencrypted_data);
+  if (ret < 0) {
+    goto err;
+  }
+
+  ret = hs_desc_decode_encrypted(desc, client_sk, &desc->encrypted_data);
   if (ret < 0) {
     goto err;
   }
diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h
index 870016432..64a5a8f7f 100644
--- a/src/feature/hs/hs_descriptor.h
+++ b/src/feature/hs/hs_descriptor.h
@@ -277,10 +277,14 @@ MOCK_DECL(int,
 
 int hs_desc_decode_descriptor(const char *encoded,
                               const uint8_t *subcredential,
+                              const curve25519_secret_key_t *client_sk,
                               hs_descriptor_t **desc_out);
 int hs_desc_decode_plaintext(const char *encoded,
                              hs_desc_plaintext_data_t *plaintext);
+int hs_desc_decode_superencrypted(const hs_descriptor_t *desc,
+                                 hs_desc_superencrypted_data_t *desc_out);
 int hs_desc_decode_encrypted(const hs_descriptor_t *desc,
+                             const curve25519_secret_key_t *client_sk,
                              hs_desc_encrypted_data_t *desc_out);
 
 size_t hs_desc_obj_size(const hs_descriptor_t *data);
@@ -324,13 +328,12 @@ STATIC int cert_is_valid(tor_cert_t *cert, uint8_t type,
 STATIC int desc_sig_is_valid(const char *b64_sig,
                              const ed25519_public_key_t *signing_pubkey,
                              const char *encoded_desc, size_t encoded_len);
-STATIC size_t decode_superencrypted(const char *message, size_t message_len,
-                                   uint8_t **encrypted_out);
 STATIC void desc_plaintext_data_free_contents(hs_desc_plaintext_data_t *desc);
 
 MOCK_DECL(STATIC size_t, decrypt_desc_layer,(const hs_descriptor_t *desc,
                                              const uint8_t *encrypted_blob,
                                              size_t encrypted_blob_size,
+                                             const uint8_t *descriptor_cookie,
                                              int is_superencrypted_layer,
                                              char **decrypted_out));
 
diff --git a/src/test/fuzz/fuzz_hsdescv3.c b/src/test/fuzz/fuzz_hsdescv3.c
index 4ec8db0a8..b332973b3 100644
--- a/src/test/fuzz/fuzz_hsdescv3.c
+++ b/src/test/fuzz/fuzz_hsdescv3.c
@@ -38,11 +38,13 @@ static size_t
 mock_decrypt_desc_layer(const hs_descriptor_t *desc,
                         const uint8_t *encrypted_blob,
                         size_t encrypted_blob_size,
+                        const uint8_t *descriptor_cookie,
                         int is_superencrypted_layer,
                         char **decrypted_out)
 {
   (void)is_superencrypted_layer;
   (void)desc;
+  (void)descriptor_cookie;
   const size_t overhead = HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN;
   if (encrypted_blob_size < overhead)
     return 0;
@@ -84,7 +86,7 @@ fuzz_main(const uint8_t *data, size_t sz)
   char *fuzzing_data = tor_memdup_nulterm(data, sz);
   memset(subcredential, 'A', sizeof(subcredential));
 
-  hs_desc_decode_descriptor(fuzzing_data, subcredential, &desc);
+  hs_desc_decode_descriptor(fuzzing_data, subcredential, NULL, &desc);
   if (desc) {
     log_debug(LD_GENERAL, "Decoding okay");
     hs_descriptor_free(desc);
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 33bb00e6e..728bb4a2f 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -390,7 +390,7 @@ test_hsdir_revision_counter_check(void *arg)
     received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
 
     retval = hs_desc_decode_descriptor(received_desc_str,
-                                       subcredential, &received_desc);
+                                       subcredential, NULL, &received_desc);
     tt_int_op(retval, OP_EQ, 0);
     tt_assert(received_desc);
 
@@ -423,7 +423,7 @@ test_hsdir_revision_counter_check(void *arg)
     received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
 
     retval = hs_desc_decode_descriptor(received_desc_str,
-                                       subcredential, &received_desc);
+                                       subcredential, NULL, &received_desc);
     tt_int_op(retval, OP_EQ, 0);
     tt_assert(received_desc);
 
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index 78cd9fa47..e003ea5ff 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -347,14 +347,15 @@ test_decode_descriptor(void *arg)
                                               subcredential);
 
   /* Give some bad stuff to the decoding function. */
-  ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential, &decoded);
+  ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential,
+                                  NULL, &decoded);
   tt_int_op(ret, OP_EQ, -1);
 
   ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
   tt_int_op(ret, OP_EQ, 0);
   tt_assert(encoded);
 
-  ret = hs_desc_decode_descriptor(encoded, subcredential, &decoded);
+  ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
   tt_int_op(ret, OP_EQ, 0);
   tt_assert(decoded);
 
@@ -375,7 +376,7 @@ test_decode_descriptor(void *arg)
     tt_int_op(ret, OP_EQ, 0);
     tt_assert(encoded);
     hs_descriptor_free(decoded);
-    ret = hs_desc_decode_descriptor(encoded, subcredential, &decoded);
+    ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
     tt_int_op(ret, OP_EQ, 0);
     tt_assert(decoded);
   }
@@ -850,103 +851,6 @@ test_build_authorized_client(void *arg)
   UNMOCK(crypto_strongest_rand);
 }
 
-/* bad desc auth type */
-static const char bad_superencrypted_text1[] = "desc-auth-type scoobysnack\n"
-  "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n"
-  "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n"
-  "encrypted\n"
-  "-----BEGIN MESSAGE-----\n"
-  "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC"
-  "BiYWQgYXQgYWxs\n"
-  "-----END MESSAGE-----\n";
-
-/* bad ephemeral key */
-static const char bad_superencrypted_text2[] = "desc-auth-type x25519\n"
-  "desc-auth-ephemeral-key differentalphabet\n"
-  "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n"
-  "encrypted\n"
-  "-----BEGIN MESSAGE-----\n"
-  "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC"
-  "BiYWQgYXQgYWxs\n"
-  "-----END MESSAGE-----\n";
-
-/* bad encrypted msg */
-static const char bad_superencrypted_text3[] = "desc-auth-type x25519\n"
-  "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n"
-  "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n"
-  "encrypted\n"
-  "-----BEGIN MESSAGE-----\n"
-  "SO SMALL NOT GOOD\n"
-  "-----END MESSAGE-----\n";
-
-static const char correct_superencrypted_text[] = "desc-auth-type x25519\n"
-  "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n"
-  "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n"
-  "auth-client Od09Qu636Qo /PKLzqewAdS/+0+vZC+MvQ dpw4NFo13zDnuPz45rxrOg\n"
-  "auth-client JRr840iGYN0 8s8cxYqF7Lx23+NducC4Qg zAafl4wPLURkuEjJreZq1g\n"
-  "encrypted\n"
-  "-----BEGIN MESSAGE-----\n"
-  "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC"
-  "BiYWQgYXQgYWxs\n"
-  "-----END MESSAGE-----\n";
-
-static const char correct_encrypted_plaintext[] = "being on mountains, "
-  "thinking about computers, is not bad at all";
-
-static void
-test_parse_hs_desc_superencrypted(void *arg)
-{
-  (void) arg;
-  size_t retval;
-  uint8_t *encrypted_out = NULL;
-
-  {
-    setup_full_capture_of_logs(LOG_WARN);
-    retval = decode_superencrypted(bad_superencrypted_text1,
-                                   strlen(bad_superencrypted_text1),
-                                   &encrypted_out);
-    tt_u64_op(retval, OP_EQ, 0);
-    tt_ptr_op(encrypted_out, OP_EQ, NULL);
-    expect_log_msg_containing("Unrecognized desc auth type");
-    teardown_capture_of_logs();
-  }
-
-  {
-    setup_full_capture_of_logs(LOG_WARN);
-    retval = decode_superencrypted(bad_superencrypted_text2,
-                                   strlen(bad_superencrypted_text2),
-                                   &encrypted_out);
-    tt_u64_op(retval, OP_EQ, 0);
-    tt_ptr_op(encrypted_out, OP_EQ, NULL);
-    expect_log_msg_containing("Bogus desc auth key in HS desc");
-    teardown_capture_of_logs();
-  }
-
-  {
-    setup_full_capture_of_logs(LOG_WARN);
-    retval = decode_superencrypted(bad_superencrypted_text3,
-                                   strlen(bad_superencrypted_text3),
-                                   &encrypted_out);
-    tt_u64_op(retval, OP_EQ, 0);
-    tt_ptr_op(encrypted_out, OP_EQ, NULL);
-    expect_log_msg_containing("Length of descriptor\'s encrypted data "
-                              "is too small.");
-    teardown_capture_of_logs();
-  }
-
-  /* Now finally the good one */
-  retval = decode_superencrypted(correct_superencrypted_text,
-                                 strlen(correct_superencrypted_text),
-                                 &encrypted_out);
-
-  tt_u64_op(retval, OP_EQ, strlen(correct_encrypted_plaintext));
-  tt_mem_op(encrypted_out, OP_EQ, correct_encrypted_plaintext,
-            strlen(correct_encrypted_plaintext));
-
- done:
-  tor_free(encrypted_out);
-}
-
 struct testcase_t hs_descriptor[] = {
   /* Encoding tests. */
   { "cert_encoding", test_cert_encoding, TT_FORK,
@@ -980,8 +884,5 @@ struct testcase_t hs_descriptor[] = {
   { "build_authorized_client", test_build_authorized_client, TT_FORK,
     NULL, NULL },
 
-  { "parse_hs_desc_superencrypted", test_parse_hs_desc_superencrypted,
-    TT_FORK, NULL, NULL },
-
   END_OF_TESTCASES
 };





More information about the tor-commits mailing list