[tor-commits] [tor/master] hs-v3: Generate all descriptor related keys

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


commit 08bbcffc0ef6e69c02cc746568724df662654d2b
Author: Suphanat Chunhapanya <haxx.pop at gmail.com>
Date:   Mon Apr 9 23:09:41 2018 +0700

    hs-v3: Generate all descriptor related keys
    
    We need to generate all the related keys when building the descriptor, so that
    we can encrypt the descriptor.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/feature/hs/hs_descriptor.c | 107 +++++++++++++++++++++++++++++++++++++++++
 src/feature/hs/hs_descriptor.h |  64 ++++++++++++++++++++++++
 src/feature/hs/hs_service.c    | 104 +++++++++++++++++++++++++++++++++++++--
 src/feature/hs/hs_service.h    |   7 +++
 4 files changed, 278 insertions(+), 4 deletions(-)

diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 392800016..34ff2b0a3 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -168,6 +168,26 @@ desc_plaintext_data_free_contents(hs_desc_plaintext_data_t *desc)
   memwipe(desc, 0, sizeof(*desc));
 }
 
+/* Free the content of the superencrypted section of a descriptor. */
+static void
+desc_superencrypted_data_free_contents(hs_desc_superencrypted_data_t *desc)
+{
+  if (!desc) {
+    return;
+  }
+
+  if (desc->encrypted_blob) {
+    tor_free(desc->encrypted_blob);
+  }
+  if (desc->clients) {
+    SMARTLIST_FOREACH(desc->clients, hs_desc_authorized_client_t *, client,
+                      hs_desc_authorized_client_free(client));
+    smartlist_free(desc->clients);
+  }
+
+  memwipe(desc, 0, sizeof(*desc));
+}
+
 /* Free the content of the encrypted section of a descriptor. */
 static void
 desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc)
@@ -2383,6 +2403,14 @@ hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc)
   tor_free(desc);
 }
 
+/* Free the descriptor plaintext data object. */
+void
+hs_desc_superencrypted_data_free_(hs_desc_superencrypted_data_t *desc)
+{
+  desc_superencrypted_data_free_contents(desc);
+  tor_free(desc);
+}
+
 /* Free the descriptor encrypted data object. */
 void
 hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc)
@@ -2400,6 +2428,7 @@ hs_descriptor_free_(hs_descriptor_t *desc)
   }
 
   desc_plaintext_data_free_contents(&desc->plaintext_data);
+  desc_superencrypted_data_free_contents(&desc->superencrypted_data);
   desc_encrypted_data_free_contents(&desc->encrypted_data);
   tor_free(desc);
 }
@@ -2475,6 +2504,84 @@ hs_desc_intro_point_free_(hs_desc_intro_point_t *ip)
   tor_free(ip);
 }
 
+/* Build a fake client info for the descriptor */
+void
+hs_desc_build_fake_authorized_client(hs_desc_authorized_client_t *client_out)
+{
+  tor_assert(client_out);
+
+  crypto_rand((char *) client_out->client_id,
+              sizeof(client_out->client_id));
+  crypto_rand((char *) client_out->iv,
+              sizeof(client_out->iv));
+  crypto_rand((char *) client_out->encrypted_cookie,
+              sizeof(client_out->encrypted_cookie));
+}
+
+/* Using the client public key, auth ephemeral secret key, and descriptor
+ * cookie, build the auth client so we can then encode the descriptor for
+ * publication. client_out must be already allocated. */
+void
+hs_desc_build_authorized_client(const curve25519_public_key_t *client_pk,
+                                const curve25519_secret_key_t *
+                                auth_ephemeral_sk,
+                                const uint8_t *descriptor_cookie,
+                                hs_desc_authorized_client_t *client_out)
+{
+  uint8_t secret_seed[CURVE25519_OUTPUT_LEN];
+  uint8_t keystream[HS_DESC_CLIENT_ID_LEN + HS_DESC_COOKIE_KEY_LEN];
+  uint8_t *cookie_key;
+  crypto_cipher_t *cipher;
+  crypto_xof_t *xof;
+
+  tor_assert(client_pk);
+  tor_assert(auth_ephemeral_sk);
+  tor_assert(descriptor_cookie);
+  tor_assert(client_out);
+  tor_assert(!tor_mem_is_zero((char *) auth_ephemeral_sk,
+                              sizeof(*auth_ephemeral_sk)));
+  tor_assert(!tor_mem_is_zero((char *) client_pk, sizeof(*client_pk)));
+  tor_assert(!tor_mem_is_zero((char *) descriptor_cookie,
+                              HS_DESC_DESCRIPTOR_COOKIE_LEN));
+
+  /* Calculate x25519(hs_y, client_X) */
+  curve25519_handshake(secret_seed,
+                       auth_ephemeral_sk,
+                       client_pk);
+
+  /* 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);
+
+  memcpy(client_out->client_id, keystream, HS_DESC_CLIENT_ID_LEN);
+  cookie_key = keystream + HS_DESC_CLIENT_ID_LEN;
+
+  /* Random IV */
+  crypto_strongest_rand(client_out->iv, sizeof(client_out->iv));
+
+  /* This creates a cipher for AES. It can't fail. */
+  cipher = crypto_cipher_new_with_iv_and_bits(cookie_key, client_out->iv,
+                                              HS_DESC_COOKIE_KEY_BIT_SIZE);
+  /* This can't fail. */
+  crypto_cipher_encrypt(cipher, (char *) client_out->encrypted_cookie,
+                        (const char *) descriptor_cookie,
+                        HS_DESC_DESCRIPTOR_COOKIE_LEN);
+
+  memwipe(secret_seed, 0, sizeof(secret_seed));
+  memwipe(keystream, 0, sizeof(keystream));
+
+  crypto_cipher_free(cipher);
+}
+
+/* Free an authoriezd client object. */
+void
+hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client)
+{
+  tor_free(client);
+}
+
 /* Free the given descriptor link specifier. */
 void
 hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls)
diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h
index bfdf7559c..3e7dcc457 100644
--- a/src/feature/hs/hs_descriptor.h
+++ b/src/feature/hs/hs_descriptor.h
@@ -59,6 +59,17 @@ struct link_specifier_t;
 #define HS_DESC_ENCRYPTED_KEY_LEN CIPHER256_KEY_LEN
 #define HS_DESC_ENCRYPTED_BIT_SIZE (HS_DESC_ENCRYPTED_KEY_LEN * 8)
 
+/* Length of each components in the auth client section in the descriptor. */
+#define HS_DESC_CLIENT_ID_LEN 8
+#define HS_DESC_DESCRIPTOR_COOKIE_LEN 16
+#define HS_DESC_COOKIE_KEY_LEN 32
+#define HS_DESC_COOKIE_KEY_BIT_SIZE (HS_DESC_COOKIE_KEY_LEN * 8)
+#define HS_DESC_ENCRYPED_COOKIE_LEN HS_DESC_DESCRIPTOR_COOKIE_LEN
+
+/* The number of auth client entries in the descriptor must be the multiple
+ * of this constant. */
+#define HS_DESC_AUTH_CLIENT_MULTIPLE 16
+
 /* Type of authentication in the descriptor. */
 typedef enum {
   HS_DESC_AUTH_ED25519 = 1
@@ -126,6 +137,20 @@ typedef struct hs_desc_intro_point_t {
   unsigned int cross_certified : 1;
 } hs_desc_intro_point_t;
 
+/* Authorized client information located in a descriptor. */
+typedef struct hs_desc_authorized_client_t {
+  /* An identifier that the client will use to identify which auth client
+   * entry it needs to use. */
+  uint8_t client_id[HS_DESC_CLIENT_ID_LEN];
+
+  /* An IV that is used to decrypt the encrypted descriptor cookie. */
+  uint8_t iv[CIPHER_IV_LEN];
+
+  /* An encrypted descriptor cookie that the client needs to decrypt to use
+   * it to decrypt the descriptor. */
+  uint8_t encrypted_cookie[HS_DESC_ENCRYPED_COOKIE_LEN];
+} hs_desc_authorized_client_t;
+
 /* The encrypted data section of a descriptor. Obviously the data in this is
  * in plaintext but encrypted once encoded. */
 typedef struct hs_desc_encrypted_data_t {
@@ -144,6 +169,24 @@ typedef struct hs_desc_encrypted_data_t {
   smartlist_t *intro_points;
 } hs_desc_encrypted_data_t;
 
+/* The superencrypted data section of a descriptor. Obviously the data in
+ * this is in plaintext but encrypted once encoded. */
+typedef struct hs_desc_superencrypted_data_t {
+  /* This field contains ephemeral x25519 public key which is used by
+   * the encryption scheme in the client authorization. */
+  curve25519_public_key_t auth_ephemeral_pubkey;
+
+  /* A list of authorized clients. Contains hs_desc_authorized_client_t
+   * objects. */
+  smartlist_t *clients;
+
+  /* Decoding only: The b64-decoded encrypted blob from the descriptor */
+  uint8_t *encrypted_blob;
+
+  /* Decoding only: Size of the encrypted_blob */
+  size_t encrypted_blob_size;
+} hs_desc_superencrypted_data_t;
+
 /* Plaintext data that is unencrypted information of the descriptor. */
 typedef struct hs_desc_plaintext_data_t {
   /* Version of the descriptor format. Spec specifies this field as a
@@ -182,6 +225,11 @@ typedef struct hs_descriptor_t {
   /* Contains the plaintext part of the descriptor. */
   hs_desc_plaintext_data_t plaintext_data;
 
+  /* The following contains what's in the superencrypted part of the
+   * descriptor. It's only encrypted in the encoded version of the descriptor
+   * thus the data contained in that object is in plaintext. */
+  hs_desc_superencrypted_data_t superencrypted_data;
+
   /* The following contains what's in the encrypted part of the descriptor.
    * It's only encrypted in the encoded version of the descriptor thus the
    * data contained in that object is in plaintext. */
@@ -211,6 +259,10 @@ void hs_descriptor_free_(hs_descriptor_t *desc);
 void hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc);
 #define hs_desc_plaintext_data_free(desc) \
   FREE_AND_NULL(hs_desc_plaintext_data_t, hs_desc_plaintext_data_free_, (desc))
+void hs_desc_superencrypted_data_free_(hs_desc_superencrypted_data_t *desc);
+#define hs_desc_superencrypted_data_free(desc) \
+  FREE_AND_NULL(hs_desc_superencrypted_data_t, \
+                hs_desc_superencrypted_data_free_, (desc))
 void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc);
 #define hs_desc_encrypted_data_free(desc) \
   FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc))
@@ -243,10 +295,22 @@ hs_desc_intro_point_t *hs_desc_intro_point_new(void);
 void hs_desc_intro_point_free_(hs_desc_intro_point_t *ip);
 #define hs_desc_intro_point_free(ip) \
   FREE_AND_NULL(hs_desc_intro_point_t, hs_desc_intro_point_free_, (ip))
+void hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client);
+#define hs_desc_authorized_client_free(client) \
+  FREE_AND_NULL(hs_desc_authorized_client_t, \
+                hs_desc_authorized_client_free_, (client))
 
 link_specifier_t *hs_desc_lspec_to_trunnel(
                                    const hs_desc_link_specifier_t *spec);
 
+void
+hs_desc_build_fake_authorized_client(hs_desc_authorized_client_t *client_out);
+void hs_desc_build_authorized_client(const curve25519_public_key_t *client_pk,
+                                     const curve25519_secret_key_t *
+                                     auth_ephemeral_sk,
+                                     const uint8_t *descriptor_cookie,
+                                     hs_desc_authorized_client_t *client_out);
+
 #ifdef HS_DESCRIPTOR_PRIVATE
 
 /* Encoding. */
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 8d1ee82ab..0ffe0926d 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -1559,6 +1559,78 @@ build_service_desc_encrypted(const hs_service_t *service,
   return 0;
 }
 
+/* Populate the descriptor superencrypted section from the given service
+ * object. This will generate a valid list of hs_desc_authorized_client_t
+ * of clients that are authorized to use the service. Return 0 on success
+ * else -1 on error. */
+static int
+build_service_desc_superencrypted(const hs_service_t *service,
+                                  hs_service_descriptor_t *desc)
+{
+  const hs_service_config_t *config;
+  int i;
+  hs_desc_superencrypted_data_t *superencrypted;
+
+  tor_assert(service);
+  tor_assert(desc);
+
+  superencrypted = &desc->desc->superencrypted_data;
+  config = &service->config;
+
+  /* The ephemeral key pair is already generated, so this should not give
+   * an error. */
+  memcpy(&superencrypted->auth_ephemeral_pubkey,
+         &desc->auth_ephemeral_kp.pubkey,
+         sizeof(curve25519_public_key_t));
+
+  /* Create a smartlist to store clients */
+  superencrypted->clients = smartlist_new();
+
+  /* We do not need to build the desc authorized client if the client
+   * authorization is disabled */
+  if (config->is_client_auth_enabled) {
+    SMARTLIST_FOREACH_BEGIN(config->clients,
+                            hs_service_authorized_client_t *, client) {
+      hs_desc_authorized_client_t *desc_client;
+      desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
+
+      /* Prepare the client for descriptor and then add to the list in the
+       * superencrypted part of the descriptor */
+      hs_desc_build_authorized_client(&client->client_pk,
+                                      &desc->auth_ephemeral_kp.seckey,
+                                      desc->descriptor_cookie, desc_client);
+      smartlist_add(superencrypted->clients, desc_client);
+
+    } SMARTLIST_FOREACH_END(client);
+  }
+
+  /* We cannot let the number of auth-clients to be zero, so we need to
+   * make it be 16. If it is already a multiple of 16, we do not need to
+   * do anything. Otherwise, add the additional ones to make it a
+   * multiple of 16. */
+  int num_clients = smartlist_len(superencrypted->clients);
+  int num_clients_to_add;
+  if (num_clients == 0) {
+    num_clients_to_add = HS_DESC_AUTH_CLIENT_MULTIPLE;
+  } else if (num_clients % HS_DESC_AUTH_CLIENT_MULTIPLE == 0) {
+    num_clients_to_add = 0;
+  } else {
+    num_clients_to_add =
+      HS_DESC_AUTH_CLIENT_MULTIPLE
+      - (num_clients % HS_DESC_AUTH_CLIENT_MULTIPLE);
+  }
+
+  for (i = 0; i < num_clients_to_add; i++) {
+    hs_desc_authorized_client_t *desc_client;
+    desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
+
+    hs_desc_build_fake_authorized_client(desc_client);
+    smartlist_add(superencrypted->clients, desc_client);
+  }
+
+  return 0;
+}
+
 /* Populate the descriptor plaintext section from the given service object.
  * The caller must make sure that the keys in the descriptors are valid that
  * is are non-zero. Return 0 on success else -1 on error. */
@@ -1624,13 +1696,14 @@ generate_ope_cipher_for_desc(const hs_service_descriptor_t *hs_desc)
 }
 
 /* For the given service and descriptor object, create the key material which
- * is the blinded keypair and the descriptor signing keypair. Return 0 on
- * success else -1 on error where the generated keys MUST be ignored. */
+ * is the blinded keypair, the descriptor signing keypair, the ephemeral
+ * keypair, and the descriptor cookie. Return 0 on success else -1 on error
+ * where the generated keys MUST be ignored. */
 static int
 build_service_desc_keys(const hs_service_t *service,
                         hs_service_descriptor_t *desc)
 {
-  int ret = 0;
+  int ret = -1;
   ed25519_keypair_t kp;
 
   tor_assert(desc);
@@ -1661,9 +1734,28 @@ build_service_desc_keys(const hs_service_t *service,
     log_warn(LD_REND, "Can't generate descriptor signing keypair for "
                       "service %s",
              safe_str_client(service->onion_address));
-    ret = -1;
+    goto end;
   }
 
+  /* No need for extra strong, this is a temporary key only for this
+   * descriptor. Nothing long term. */
+  if (curve25519_keypair_generate(&desc->auth_ephemeral_kp, 0) < 0) {
+    log_warn(LD_REND, "Can't generate auth ephemeral keypair for "
+                      "service %s",
+             safe_str_client(service->onion_address));
+    goto end;
+  }
+
+  /* Random a descriptor cookie to be used as a part of a key to encrypt the
+   * descriptor, if the client auth is enabled. */
+  if (service->config.is_client_auth_enabled) {
+    crypto_strongest_rand(desc->descriptor_cookie,
+                          sizeof(desc->descriptor_cookie));
+  }
+
+  /* Success. */
+  ret = 0;
+ end:
   return ret;
 }
 
@@ -1697,6 +1789,10 @@ build_service_descriptor(hs_service_t *service, time_t now,
   if (build_service_desc_plaintext(service, desc, now) < 0) {
     goto err;
   }
+  /* Setup superencrypted descriptor content. */
+  if (build_service_desc_superencrypted(service, desc) < 0) {
+    goto err;
+  }
   /* Setup encrypted descriptor content. */
   if (build_service_desc_encrypted(service, desc) < 0) {
     goto err;
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index cab9b41bc..f1b98b805 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -105,6 +105,13 @@ typedef struct hs_service_descriptor_t {
    * publishes the descriptor. */
   hs_descriptor_t *desc;
 
+  /* Client authorization ephemeral keypair. */
+  curve25519_keypair_t auth_ephemeral_kp;
+
+  /* Descriptor cookie used to encrypt the descriptor, when the client
+   * authorization is enabled */
+  uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
+
   /* Descriptor signing keypair. */
   ed25519_keypair_t signing_kp;
 





More information about the tor-commits mailing list