[tor-commits] [tor/master] prop224: Implement decoding of superencrypted HS descriptor.

nickm at torproject.org nickm at torproject.org
Mon Mar 13 20:21:27 UTC 2017


commit d0fe199269addb9cab5070691a8b3be186d49986
Author: George Kadianakis <desnacked at riseup.net>
Date:   Wed Feb 8 14:42:12 2017 +0200

    prop224: Implement decoding of superencrypted HS descriptor.
    
    [Consider starting review from desc_decrypt_all() ]
---
 src/or/hs_descriptor.c | 226 ++++++++++++++++++++++++++++++++++++++++++-------
 src/or/hs_descriptor.h |   2 +
 src/or/parsecommon.h   |   4 +
 3 files changed, 200 insertions(+), 32 deletions(-)

diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index db15823..54788e6 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -113,6 +113,15 @@ static token_rule_t hs_desc_v3_token_table[] = {
   END_OF_TABLE
 };
 
+/* Descriptor ruleset for the superencrypted section. */
+static token_rule_t hs_desc_superencrypted_v3_token_table[] = {
+  T1_START(str_desc_auth_type, R3_DESC_AUTH_TYPE, GE(1), NO_OBJ),
+  T1(str_desc_auth_key, R3_DESC_AUTH_KEY, GE(1), NO_OBJ),
+  T1N(str_desc_auth_client, R3_DESC_AUTH_CLIENT, GE(3), NO_OBJ),
+  T1(str_encrypted, R3_ENCRYPTED, NO_ARGS, NEED_OBJ),
+  END_OF_TABLE
+};
+
 /* Descriptor ruleset for the encrypted section. */
 static token_rule_t hs_desc_encrypted_v3_token_table[] = {
   T1_START(str_create2_formats, R3_CREATE2_FORMATS, CONCAT_ARGS, NO_OBJ),
@@ -1313,12 +1322,17 @@ encrypted_data_length_is_valid(size_t len)
   return 0;
 }
 
-/* Decrypt the encrypted section of the descriptor using the given descriptor
- * object desc. A newly allocated NUL terminated string is put in
- * decrypted_out. Return the length of decrypted_out on success else 0 is
- * returned and decrypted_out is set to NULL. */
+/** 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. */
 static size_t
-desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
+decrypt_desc_layer(const hs_descriptor_t *desc,
+                   const uint8_t *encrypted_blob,
+                   size_t encrypted_blob_size,
+                   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];
@@ -1328,41 +1342,33 @@ desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
 
   tor_assert(decrypted_out);
   tor_assert(desc);
-  tor_assert(desc->plaintext_data.superencrypted_blob);
+  tor_assert(encrypted_blob);
 
-  /* Construction is as follow: SALT | ENCRYPTED_DATA | MAC */
-  if (!encrypted_data_length_is_valid(
-                desc->plaintext_data.superencrypted_blob_size)) {
+  /* Construction is as follow: SALT | ENCRYPTED_DATA | MAC .
+   * Make sure we have enough space for all these things. */
+  if (!encrypted_data_length_is_valid(encrypted_blob_size)) {
     goto err;
   }
 
   /* Start of the blob thus the salt. */
-  salt = desc->plaintext_data.superencrypted_blob;
+  salt = encrypted_blob;
+
   /* Next is the encrypted data. */
-  encrypted = desc->plaintext_data.superencrypted_blob +
-    HS_DESC_ENCRYPTED_SALT_LEN;
-  encrypted_len = desc->plaintext_data.superencrypted_blob_size -
+  encrypted = encrypted_blob + HS_DESC_ENCRYPTED_SALT_LEN;
+  encrypted_len = encrypted_blob_size -
     (HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN);
+  tor_assert(encrypted_len > 0); /* guaranteed by the check above */
 
-  /* At the very end is the MAC. Make sure it's of the right size. */
-  {
-    desc_mac = encrypted + encrypted_len;
-    size_t desc_mac_size = desc->plaintext_data.superencrypted_blob_size -
-                           (desc_mac - desc->plaintext_data.superencrypted_blob);
-    if (desc_mac_size != DIGEST256_LEN) {
-      log_warn(LD_REND, "Service descriptor MAC length of encrypted data "
-                        "is invalid (%lu, expected %u)",
-               (unsigned long) desc_mac_size, DIGEST256_LEN);
-      goto err;
-    }
-  }
+  /* And last comes the MAC. */
+  desc_mac = encrypted_blob + encrypted_blob_size - DIGEST256_LEN;
 
   /* 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. */
   build_secret_key_iv_mac(desc, salt, HS_DESC_ENCRYPTED_SALT_LEN,
                           secret_key, sizeof(secret_key),
                           secret_iv, sizeof(secret_iv),
-                          mac_key, sizeof(mac_key));
+                          mac_key, sizeof(mac_key),
+                          is_superencrypted_layer);
 
   /* Build MAC. */
   build_mac(mac_key, sizeof(mac_key), salt, HS_DESC_ENCRYPTED_SALT_LEN,
@@ -1392,7 +1398,7 @@ desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
   }
 
   {
-    /* Adjust length to remove NULL padding bytes */
+    /* Adjust length to remove NUL padding bytes */
     uint8_t *end = memchr(decrypted, 0, encrypted_len);
     result_len = encrypted_len;
     if (end) {
@@ -1418,6 +1424,161 @@ desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
   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")) {
+      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)) {
+    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. */
+    *encrypted_out = tor_memdup(tok->object_body, tok->object_size);
+    retval = 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. */
+static size_t
+desc_decrypt_all(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. */
+
+  /* 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);
+  if (!superencrypted_len) {
+    log_warn(LD_REND, "Decrypting superencrypted desc failed.");
+    goto err;
+  }
+  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);
+
+  /* 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) {
+    log_warn(LD_REND, "Decrypting encrypted desc failed.");
+    goto err;
+  }
+  tor_assert(decrypted_desc);
+
+  *decrypted_out = decrypted_desc;
+
+ err:
+  tor_free(superencrypted_plaintext);
+  tor_free(encrypted_blob);
+
+  return decrypted_len;
+}
+
 /* Given the start of a section and the end of it, decode a single
  * introduction point from that section. Return a newly allocated introduction
  * point object containing the decoded data. Return NULL if the section can't
@@ -1550,7 +1711,9 @@ decode_introduction_point(const hs_descriptor_t *desc, const char *start)
   tor_cert_free(cross_cert);
   SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
   smartlist_free(tokens);
-  memarea_drop_all(area);
+  if (area) {
+    memarea_drop_all(area);
+  }
 
   return ip;
 }
@@ -1804,10 +1967,9 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
   tor_assert(desc);
   tor_assert(desc_encrypted_out);
 
-  /* Decrypt the encrypted data that is located in the plaintext section in
-   * the descriptor as a blob of bytes. The following functions will use the
-   * keys found in the same section. */
-  message_len = desc_decrypt_data_v3(desc, &message);
+  /* Decrypt the superencrypted data that is located in the plaintext section
+   * in the descriptor as a blob of bytes. */
+  message_len = desc_decrypt_all(desc, &message);
   if (!message_len) {
     log_warn(LD_REND, "Service descriptor decryption failed.");
     goto err;
diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h
index 4e0e866..711514f 100644
--- a/src/or/hs_descriptor.h
+++ b/src/or/hs_descriptor.h
@@ -228,6 +228,8 @@ 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 void desc_intro_point_free(hs_desc_intro_point_t *ip);
+STATIC size_t decode_superencrypted(const char *message, size_t message_len,
+                                   uint8_t **encrypted_out);
 #endif /* HS_DESCRIPTOR_PRIVATE */
 
 #endif /* TOR_HS_DESCRIPTOR_H */
diff --git a/src/or/parsecommon.h b/src/or/parsecommon.h
index ec62bad..0af9dfb 100644
--- a/src/or/parsecommon.h
+++ b/src/or/parsecommon.h
@@ -163,6 +163,10 @@ typedef enum {
   R3_INTRO_AUTH_KEY,
   R3_INTRO_ENC_KEY,
   R3_INTRO_ENC_KEY_CERTIFICATION,
+  R3_DESC_AUTH_TYPE,
+  R3_DESC_AUTH_KEY,
+  R3_DESC_AUTH_CLIENT,
+  R3_ENCRYPTED,
 
   R_IPO_IDENTIFIER,
   R_IPO_IP_ADDRESS,





More information about the tor-commits mailing list