tor-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2018
- 17 participants
- 3230 discussions

07 Sep '18
commit 0dab4ac2dde6431913761e8365b0d21174b3181e
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Fri Apr 13 04:52:46 2018 +0700
test: HS v3 building a descriptor with client auth
This commit tests that the descriptor building result, when the client
authorization is enabled, includes everything that is needed.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/test/test_hs_service.c | 124 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 124 insertions(+)
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 573c8bc56..11f542c44 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -228,6 +228,24 @@ helper_create_origin_circuit(int purpose, int flags)
return circ;
}
+/* Helper: Return a newly allocated authorized client object with
+ * and a newly generated public key. */
+static hs_service_authorized_client_t *
+helper_create_authorized_client(void)
+{
+ int ret;
+ hs_service_authorized_client_t *client;
+ curve25519_secret_key_t seckey;
+ client = tor_malloc_zero(sizeof(hs_service_authorized_client_t));
+
+ ret = curve25519_secret_key_generate(&seckey, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ curve25519_public_key_generate(&client->client_pk, &seckey);
+
+ done:
+ return client;
+}
+
/* Helper: Return a newly allocated service object with the identity keypair
* sets and the current descriptor. Then register it to the global map.
* Caller should us hs_free_all() to free this service or remove it from the
@@ -252,6 +270,26 @@ helper_create_service(void)
return service;
}
+/* Helper: Return a newly allocated service object with clients. */
+static hs_service_t *
+helper_create_service_with_clients(int num_clients)
+{
+ int i;
+ hs_service_t *service = helper_create_service();
+ tt_assert(service);
+ service->config.is_client_auth_enabled = 1;
+ service->config.clients = smartlist_new();
+
+ for (i = 0; i < num_clients; i++) {
+ hs_service_authorized_client_t *client;
+ client = helper_create_authorized_client();
+ smartlist_add(service->config.clients, client);
+ }
+
+ done:
+ return service;
+}
+
/* Helper: Return a newly allocated service intro point with two link
* specifiers, one IPv4 and one legacy ID set to As. */
static hs_service_intro_point_t *
@@ -1562,6 +1600,90 @@ test_build_update_descriptors(void *arg)
nodelist_free_all();
}
+/** Test building descriptors. We use this separate function instead of
+ * using test_build_update_descriptors because that function is too complex
+ * and also too interactive. */
+static void
+test_build_descriptors(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(get_or_state,
+ get_or_state_replacement);
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 04:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after);
+
+ /* Generate a valid number of fake auth clients when a client authorization
+ * is disabled. */
+ {
+ hs_service_t *service = helper_create_service();
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16);
+ }
+
+ /* Generate a valid number of fake auth clients when the number of
+ * clients is zero. */
+ {
+ hs_service_t *service = helper_create_service_with_clients(0);
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16);
+ }
+
+ /* Generate a valid number of fake auth clients when the number of
+ * clients is not a multiple of 16. */
+ {
+ hs_service_t *service = helper_create_service_with_clients(20);
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32);
+ }
+
+ /* Do not generate any fake desc client when the number of clients is
+ * a multiple of 16 but not zero. */
+ {
+ hs_service_t *service = helper_create_service_with_clients(32);
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32);
+ }
+
+ done:
+ hs_free_all();
+}
+
static void
test_upload_descriptors(void *arg)
{
@@ -1780,6 +1902,8 @@ struct testcase_t hs_service_tests[] = {
NULL, NULL },
{ "build_update_descriptors", test_build_update_descriptors, TT_FORK,
NULL, NULL },
+ { "build_descriptors", test_build_descriptors, TT_FORK,
+ NULL, NULL },
{ "upload_descriptors", test_upload_descriptors, TT_FORK,
NULL, NULL },
{ "rendezvous1_parsing", test_rendezvous1_parsing, TT_FORK,
1
0

[tor/master] test: Build an HSv3 descriptor with authorized client
by nickm@torproject.org 07 Sep '18
by nickm@torproject.org 07 Sep '18
07 Sep '18
commit 10f4c46e50478e7620f3c23413ddd292d883e8ca
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Fri Apr 13 04:56:17 2018 +0700
test: Build an HSv3 descriptor with authorized client
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/lib/crypt_ops/crypto_rand.c | 4 +--
src/lib/crypt_ops/crypto_rand.h | 2 +-
src/test/test_hs_descriptor.c | 70 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c
index fb9d0c2c6..554777cf5 100644
--- a/src/lib/crypt_ops/crypto_rand.c
+++ b/src/lib/crypt_ops/crypto_rand.c
@@ -319,8 +319,8 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
* Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
* storing it into <b>out</b>.
**/
-void
-crypto_strongest_rand(uint8_t *out, size_t out_len)
+MOCK_IMPL(void,
+crypto_strongest_rand,(uint8_t *out, size_t out_len))
{
#define DLEN SHA512_DIGEST_LENGTH
/* We're going to hash DLEN bytes from the system RNG together with some
diff --git a/src/lib/crypt_ops/crypto_rand.h b/src/lib/crypt_ops/crypto_rand.h
index 938f11909..25bcfa1f1 100644
--- a/src/lib/crypt_ops/crypto_rand.h
+++ b/src/lib/crypt_ops/crypto_rand.h
@@ -21,7 +21,7 @@
int crypto_seed_rng(void) ATTR_WUR;
MOCK_DECL(void,crypto_rand,(char *to, size_t n));
void crypto_rand_unmocked(char *to, size_t n);
-void crypto_strongest_rand(uint8_t *out, size_t out_len);
+MOCK_DECL(void,crypto_strongest_rand,(uint8_t *out, size_t out_len));
int crypto_rand_int(unsigned int max);
int crypto_rand_int_range(unsigned int min, unsigned int max);
uint64_t crypto_rand_uint64_range(uint64_t min, uint64_t max);
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index 9a7e66eae..9191b74d9 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -30,6 +30,13 @@ DISABLE_GCC_WARNING(overlength-strings)
#include "test_hs_descriptor.inc"
ENABLE_GCC_WARNING(overlength-strings)
+/* Mock function to fill all bytes with 1 */
+static void
+mock_crypto_strongest_rand(uint8_t *out, size_t out_len)
+{
+ memset(out, 1, out_len);
+}
+
/* Test certificate encoding put in a descriptor. */
static void
test_cert_encoding(void *arg)
@@ -764,6 +771,67 @@ test_desc_signature(void *arg)
tor_free(data);
}
+static void
+test_build_authorized_client(void *arg)
+{
+ int ret;
+ hs_desc_authorized_client_t *desc_client = NULL;
+ uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
+ curve25519_secret_key_t auth_ephemeral_sk;
+ curve25519_secret_key_t client_sk;
+ curve25519_public_key_t client_pk;
+ const char ephemeral_sk_b16[] =
+ "d023b674d993a5c8446bd2ca97e9961149b3c0e88c7dc14e8777744dd3468d6a";
+ const char descriptor_cookie_b16[] =
+ "07d087f1d8c68393721f6e70316d3b29";
+ const char client_pubkey_b16[] =
+ "8c1298fa6050e372f8598f6deca32e27b0ad457741422c2629ebb132cf7fae37";
+ char *mem_op_hex_tmp=NULL;
+
+ (void) arg;
+
+ ret = curve25519_secret_key_generate(&auth_ephemeral_sk, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = curve25519_secret_key_generate(&client_sk, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ curve25519_public_key_generate(&client_pk, &client_sk);
+
+ desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
+
+ base16_decode((char *) &auth_ephemeral_sk,
+ sizeof(auth_ephemeral_sk),
+ ephemeral_sk_b16,
+ strlen(ephemeral_sk_b16));
+
+ base16_decode((char *) descriptor_cookie,
+ sizeof(descriptor_cookie),
+ descriptor_cookie_b16,
+ strlen(descriptor_cookie_b16));
+
+ base16_decode((char *) &client_pk,
+ sizeof(client_pk),
+ client_pubkey_b16,
+ strlen(client_pubkey_b16));
+
+ MOCK(crypto_strongest_rand, mock_crypto_strongest_rand);
+
+ hs_desc_build_authorized_client(&client_pk, &auth_ephemeral_sk,
+ descriptor_cookie, desc_client);
+
+ test_memeq_hex((char *) desc_client->client_id,
+ "b514ef67192cad5f");
+ test_memeq_hex((char *) desc_client->iv,
+ "01010101010101010101010101010101");
+ test_memeq_hex((char *) desc_client->encrypted_cookie,
+ "46860a9df37b9f6d708E0D7E730C10C1");
+
+ done:
+ tor_free(desc_client);
+ tor_free(mem_op_hex_tmp);
+ 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"
@@ -891,6 +959,8 @@ struct testcase_t hs_descriptor[] = {
NULL, NULL },
{ "desc_signature", test_desc_signature, TT_FORK,
NULL, NULL },
+ { "build_authorized_client", test_build_authorized_client, TT_FORK,
+ NULL, NULL },
{ "parse_hs_desc_superencrypted", test_parse_hs_desc_superencrypted,
TT_FORK, NULL, NULL },
1
0

[tor/master] test: HS v3 descriptor encoding with client authorization
by nickm@torproject.org 07 Sep '18
by nickm@torproject.org 07 Sep '18
07 Sep '18
commit fd6bec923c16004ce106d634187f12b57f220b91
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Sat Apr 14 17:50:07 2018 +0700
test: HS v3 descriptor encoding with client authorization
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/test/test_hs_descriptor.c | 27 ++++++++++++++++++++++-----
1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index bc72b34c0..78cd9fa47 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -291,7 +291,6 @@ static void
test_encode_descriptor(void *arg)
{
int ret;
- char *encoded = NULL;
ed25519_keypair_t signing_kp;
hs_descriptor_t *desc = NULL;
@@ -300,13 +299,31 @@ test_encode_descriptor(void *arg)
ret = ed25519_keypair_generate(&signing_kp, 0);
tt_int_op(ret, OP_EQ, 0);
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
- ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
- tt_int_op(ret, OP_EQ, 0);
- tt_assert(encoded);
+ {
+ char *encoded = NULL;
+ ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(encoded);
+
+ tor_free(encoded);
+ }
+
+ {
+ char *encoded = NULL;
+ uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
+
+ crypto_strongest_rand(descriptor_cookie, sizeof(descriptor_cookie));
+
+ ret = hs_desc_encode_descriptor(desc, &signing_kp,
+ descriptor_cookie, &encoded);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(encoded);
+
+ tor_free(encoded);
+ }
done:
hs_descriptor_free(desc);
- tor_free(encoded);
}
static void
1
0

07 Sep '18
commit fa50aee3663b6f6dca61e330df59af6d8c035fe4
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Sat Apr 14 04:04:31 2018 +0700
hs-v3: Encrypt the descriptor using a cookie
Previously, we encrypted the descriptor without the descriptor cookie. This
commit, when the client auth is enabled, the descriptor cookie is always used.
I also removed the code that is used to generate fake auth clients because it
will not be used anymore.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/feature/hs/hs_descriptor.c | 285 ++++++++++++++++++++++++++---------------
src/feature/hs/hs_descriptor.h | 7 +-
src/feature/hs/hs_service.c | 39 +++++-
src/test/hs_test_helpers.c | 19 +++
src/test/test_hs_cache.c | 16 +--
src/test/test_hs_client.c | 2 +-
src/test/test_hs_common.c | 6 +-
src/test/test_hs_descriptor.c | 7 +-
8 files changed, 256 insertions(+), 125 deletions(-)
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 34ff2b0a3..b99797497 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -240,53 +240,72 @@ build_mac(const uint8_t *mac_key, size_t mac_key_len,
crypto_digest_free(digest);
}
-/* Using a given decriptor object, build the secret input needed for the
- * KDF and put it in the dst pointer which is an already allocated buffer
- * of size dstlen. */
-static void
-build_secret_input(const hs_descriptor_t *desc, uint8_t *dst, size_t dstlen)
+/* Using a secret data and a given decriptor object, build the secret
+ * input needed for the KDF.
+ *
+ * secret_input = SECRET_DATA | subcredential | INT_8(revision_counter)
+ *
+ * Then, set the newly allocated buffer in secret_input_out and return the
+ * length of the buffer. */
+static size_t
+build_secret_input(const hs_descriptor_t *desc,
+ const uint8_t *secret_data,
+ size_t secret_data_len,
+ uint8_t **secret_input_out)
{
size_t offset = 0;
+ size_t secret_input_len = secret_data_len + DIGEST256_LEN + sizeof(uint64_t);
+ uint8_t *secret_input = NULL;
tor_assert(desc);
- tor_assert(dst);
- tor_assert(HS_DESC_ENCRYPTED_SECRET_INPUT_LEN <= dstlen);
-
- /* XXX use the destination length as the memcpy length */
- /* Copy blinded public key. */
- memcpy(dst, desc->plaintext_data.blinded_pubkey.pubkey,
- sizeof(desc->plaintext_data.blinded_pubkey.pubkey));
- offset += sizeof(desc->plaintext_data.blinded_pubkey.pubkey);
+ tor_assert(secret_data);
+ tor_assert(secret_input_out);
+
+ secret_input = tor_malloc_zero(secret_input_len);
+
+ /* Copy the secret data. */
+ memcpy(secret_input, secret_data, secret_data_len);
+ offset += secret_data_len;
/* Copy subcredential. */
- memcpy(dst + offset, desc->subcredential, sizeof(desc->subcredential));
- offset += sizeof(desc->subcredential);
+ memcpy(secret_input + offset, desc->subcredential, DIGEST256_LEN);
+ offset += DIGEST256_LEN;
/* Copy revision counter value. */
- set_uint64(dst + offset, tor_htonll(desc->plaintext_data.revision_counter));
+ set_uint64(secret_input + offset,
+ tor_htonll(desc->plaintext_data.revision_counter));
offset += sizeof(uint64_t);
- tor_assert(HS_DESC_ENCRYPTED_SECRET_INPUT_LEN == offset);
+ tor_assert(secret_input_len == offset);
+
+ *secret_input_out = secret_input;
+
+ return secret_input_len;
}
/* Do the KDF construction and put the resulting data in key_out which is of
* key_out_len length. It uses SHAKE-256 as specified in the spec. */
static void
build_kdf_key(const hs_descriptor_t *desc,
+ const uint8_t *secret_data,
+ size_t secret_data_len,
const uint8_t *salt, size_t salt_len,
uint8_t *key_out, size_t key_out_len,
int is_superencrypted_layer)
{
- uint8_t secret_input[HS_DESC_ENCRYPTED_SECRET_INPUT_LEN];
+ uint8_t *secret_input = NULL;
+ size_t secret_input_len;
crypto_xof_t *xof;
tor_assert(desc);
+ tor_assert(secret_data);
tor_assert(salt);
tor_assert(key_out);
/* Build the secret input for the KDF computation. */
- build_secret_input(desc, secret_input, sizeof(secret_input));
+ secret_input_len = build_secret_input(desc, secret_data,
+ secret_data_len, &secret_input);
xof = crypto_xof_new();
/* Feed our KDF. [SHAKE it like a polaroid picture --Yawning]. */
- crypto_xof_add_bytes(xof, secret_input, sizeof(secret_input));
+ crypto_xof_add_bytes(xof, secret_input, secret_input_len);
crypto_xof_add_bytes(xof, salt, salt_len);
/* Feed in the right string constant based on the desc layer */
@@ -301,14 +320,18 @@ build_kdf_key(const hs_descriptor_t *desc,
/* Eat from our KDF. */
crypto_xof_squeeze_bytes(xof, key_out, key_out_len);
crypto_xof_free(xof);
- memwipe(secret_input, 0, sizeof(secret_input));
+ memwipe(secret_input, 0, secret_input_len);
+
+ tor_free(secret_input);
}
-/* Using the given descriptor and salt, run it through our KDF function and
- * then extract a secret key in key_out, the IV in iv_out and MAC in mac_out.
- * This function can't fail. */
+/* Using the given descriptor, secret data, and salt, run it through our
+ * KDF function and then extract a secret key in key_out, the IV in iv_out
+ * and MAC in mac_out. This function can't fail. */
static void
build_secret_key_iv_mac(const hs_descriptor_t *desc,
+ const uint8_t *secret_data,
+ size_t secret_data_len,
const uint8_t *salt, size_t salt_len,
uint8_t *key_out, size_t key_len,
uint8_t *iv_out, size_t iv_len,
@@ -319,12 +342,14 @@ build_secret_key_iv_mac(const hs_descriptor_t *desc,
uint8_t kdf_key[HS_DESC_ENCRYPTED_KDF_OUTPUT_LEN];
tor_assert(desc);
+ tor_assert(secret_data);
tor_assert(salt);
tor_assert(key_out);
tor_assert(iv_out);
tor_assert(mac_out);
- build_kdf_key(desc, salt, salt_len, kdf_key, sizeof(kdf_key),
+ build_kdf_key(desc, secret_data, secret_data_len,
+ salt, salt_len, kdf_key, sizeof(kdf_key),
is_superencrypted_layer);
/* Copy the bytes we need for both the secret key and IV. */
memcpy(key_out, kdf_key, key_len);
@@ -630,12 +655,15 @@ build_encrypted(const uint8_t *key, const uint8_t *iv, const char *plaintext,
return encrypted_len;
}
-/* Encrypt the given <b>plaintext</b> buffer using <b>desc</b> to get the
- * keys. Set encrypted_out with the encrypted data and return the length of
- * it. <b>is_superencrypted_layer</b> is set if this is the outer encrypted
- * layer of the descriptor. */
+/* Encrypt the given <b>plaintext</b> buffer using <b>desc</b> and
+ * <b>secret_data</b> to get the keys. Set encrypted_out with the encrypted
+ * data and return the length of it. <b>is_superencrypted_layer</b> is set
+ * if this is the outer encrypted layer of the descriptor. */
static size_t
-encrypt_descriptor_data(const hs_descriptor_t *desc, const char *plaintext,
+encrypt_descriptor_data(const hs_descriptor_t *desc,
+ const uint8_t *secret_data,
+ size_t secret_data_len,
+ const char *plaintext,
char **encrypted_out, int is_superencrypted_layer)
{
char *final_blob;
@@ -646,6 +674,7 @@ encrypt_descriptor_data(const hs_descriptor_t *desc, const char *plaintext,
uint8_t mac_key[DIGEST256_LEN], mac[DIGEST256_LEN];
tor_assert(desc);
+ tor_assert(secret_data);
tor_assert(plaintext);
tor_assert(encrypted_out);
@@ -654,7 +683,8 @@ encrypt_descriptor_data(const hs_descriptor_t *desc, const char *plaintext,
/* KDF construction resulting in a key from which the secret key, IV and MAC
* key are extracted which is what we need for the encryption. */
- build_secret_key_iv_mac(desc, salt, sizeof(salt),
+ build_secret_key_iv_mac(desc, secret_data, secret_data_len,
+ salt, sizeof(salt),
secret_key, sizeof(secret_key),
secret_iv, sizeof(secret_iv),
mac_key, sizeof(mac_key),
@@ -695,69 +725,65 @@ encrypt_descriptor_data(const hs_descriptor_t *desc, const char *plaintext,
return final_blob_len;
}
-/* Create and return a string containing a fake client-auth entry. It's the
- * responsibility of the caller to free the returned string. This function will
- * never fail. */
+/* Create and return a string containing a client-auth entry. It's the
+ * responsibility of the caller to free the returned string. This function
+ * will never fail. */
static char *
-get_fake_auth_client_str(void)
+get_auth_client_str(const hs_desc_authorized_client_t *client)
{
+ int ret;
char *auth_client_str = NULL;
- /* We are gonna fill these arrays with fake base64 data. They are all double
+ /* We are gonna fill these arrays with base64 data. They are all double
* the size of their binary representation to fit the base64 overhead. */
- char client_id_b64[8*2];
- char iv_b64[16*2];
- char encrypted_cookie_b64[16*2];
- int retval;
-
- /* This is a macro to fill a field with random data and then base64 it. */
-#define FILL_WITH_FAKE_DATA_AND_BASE64(field) STMT_BEGIN \
- crypto_rand((char *)field, sizeof(field)); \
- retval = base64_encode_nopad(field##_b64, sizeof(field##_b64), \
- field, sizeof(field)); \
- tor_assert(retval > 0); \
+ char client_id_b64[HS_DESC_CLIENT_ID_LEN * 2];
+ char iv_b64[CIPHER_IV_LEN * 2];
+ char encrypted_cookie_b64[HS_DESC_ENCRYPED_COOKIE_LEN * 2];
+
+#define ASSERT_AND_BASE64(field) STMT_BEGIN \
+ tor_assert(!tor_mem_is_zero((char *) client->field, \
+ sizeof(client->field))); \
+ ret = base64_encode_nopad(field##_b64, sizeof(field##_b64), \
+ client->field, sizeof(client->field)); \
+ tor_assert(ret > 0); \
STMT_END
- { /* Get those fakes! */
- uint8_t client_id[8]; /* fake client-id */
- uint8_t iv[16]; /* fake IV (initialization vector) */
- uint8_t encrypted_cookie[16]; /* fake encrypted cookie */
-
- FILL_WITH_FAKE_DATA_AND_BASE64(client_id);
- FILL_WITH_FAKE_DATA_AND_BASE64(iv);
- FILL_WITH_FAKE_DATA_AND_BASE64(encrypted_cookie);
- }
+ ASSERT_AND_BASE64(client_id);
+ ASSERT_AND_BASE64(iv);
+ ASSERT_AND_BASE64(encrypted_cookie);
/* Build the final string */
tor_asprintf(&auth_client_str, "%s %s %s %s", str_desc_auth_client,
client_id_b64, iv_b64, encrypted_cookie_b64);
-#undef FILL_WITH_FAKE_DATA_AND_BASE64
+#undef ASSERT_AND_BASE64
return auth_client_str;
}
-/** How many lines of "client-auth" we want in our descriptors; fake or not. */
-#define CLIENT_AUTH_ENTRIES_BLOCK_SIZE 16
-
/** Create the "client-auth" part of the descriptor and return a
* newly-allocated string with it. It's the responsibility of the caller to
* free the returned string. */
static char *
-get_fake_auth_client_lines(void)
+get_all_auth_client_lines(const hs_descriptor_t *desc)
{
- /* XXX: Client authorization is still not implemented, so all this function
- does is make fake clients */
- int i = 0;
smartlist_t *auth_client_lines = smartlist_new();
char *auth_client_lines_str = NULL;
- /* Make a line for each fake client */
- const int num_fake_clients = CLIENT_AUTH_ENTRIES_BLOCK_SIZE;
- for (i = 0; i < num_fake_clients; i++) {
- char *auth_client_str = get_fake_auth_client_str();
- tor_assert(auth_client_str);
+ tor_assert(desc);
+ tor_assert(desc->superencrypted_data.clients);
+ tor_assert(smartlist_len(desc->superencrypted_data.clients) != 0);
+ tor_assert(smartlist_len(desc->superencrypted_data.clients)
+ % HS_DESC_AUTH_CLIENT_MULTIPLE == 0);
+
+ /* Make a line for each client */
+ SMARTLIST_FOREACH_BEGIN(desc->superencrypted_data.clients,
+ const hs_desc_authorized_client_t *, client) {
+ char *auth_client_str = NULL;
+
+ auth_client_str = get_auth_client_str(client);
+
smartlist_add(auth_client_lines, auth_client_str);
- }
+ } SMARTLIST_FOREACH_END(client);
/* Join all lines together to form final string */
auth_client_lines_str = smartlist_join_strings(auth_client_lines,
@@ -837,32 +863,29 @@ get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
char *layer1_str = NULL;
smartlist_t *lines = smartlist_new();
- /* XXX: Disclaimer: This function generates only _fake_ client auth
- * data. Real client auth is not yet implemented, but client auth data MUST
- * always be present in descriptors. In the future this function will be
- * refactored to use real client auth data if they exist (#20700). */
- (void) *desc;
-
/* Specify auth type */
smartlist_add_asprintf(lines, "%s %s\n", str_desc_auth_type, "x25519");
- { /* Create fake ephemeral x25519 key */
- char fake_key_base64[CURVE25519_BASE64_PADDED_LEN + 1];
- curve25519_keypair_t fake_x25519_keypair;
- if (curve25519_keypair_generate(&fake_x25519_keypair, 0) < 0) {
- goto done;
- }
- if (curve25519_public_to_base64(fake_key_base64,
- &fake_x25519_keypair.pubkey) < 0) {
+ { /* Print ephemeral x25519 key */
+ char ephemeral_key_base64[CURVE25519_BASE64_PADDED_LEN + 1];
+ const curve25519_public_key_t *ephemeral_pubkey;
+
+ ephemeral_pubkey = &desc->superencrypted_data.auth_ephemeral_pubkey;
+ tor_assert(!tor_mem_is_zero((char *) ephemeral_pubkey->public_key,
+ CURVE25519_PUBKEY_LEN));
+
+ if (curve25519_public_to_base64(ephemeral_key_base64,
+ ephemeral_pubkey) < 0) {
goto done;
}
smartlist_add_asprintf(lines, "%s %s\n",
- str_desc_auth_key, fake_key_base64);
- /* No need to memwipe any of these fake keys. They will go unused. */
+ str_desc_auth_key, ephemeral_key_base64);
+
+ memwipe(ephemeral_key_base64, 0, sizeof(ephemeral_key_base64));
}
- { /* Create fake auth-client lines. */
- char *auth_client_lines = get_fake_auth_client_lines();
+ { /* Create auth-client lines. */
+ char *auth_client_lines = get_all_auth_client_lines(desc);
tor_assert(auth_client_lines);
smartlist_add(lines, auth_client_lines);
}
@@ -880,6 +903,8 @@ get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
layer1_str = smartlist_join_strings(lines, "", 0, NULL);
done:
+ /* We need to memwipe all lines because it contains the ephemeral key */
+ SMARTLIST_FOREACH(lines, char *, a, memwipe(a, 0, strlen(a)));
SMARTLIST_FOREACH(lines, char *, a, tor_free(a));
smartlist_free(lines);
@@ -888,11 +913,14 @@ get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
/* Encrypt <b>encoded_str</b> into an encrypted blob and then base64 it before
* returning it. <b>desc</b> is provided to derive the encryption
- * keys. <b>is_superencrypted_layer</b> is set if <b>encoded_str</b> is the
+ * keys. <b>secret_data</b> is also proved to derive the encryption keys.
+ * <b>is_superencrypted_layer</b> is set if <b>encoded_str</b> is the
* middle (superencrypted) layer of the descriptor. It's the responsibility of
* the caller to free the returned string. */
static char *
encrypt_desc_data_and_base64(const hs_descriptor_t *desc,
+ const uint8_t *secret_data,
+ size_t secret_data_len,
const char *encoded_str,
int is_superencrypted_layer)
{
@@ -900,7 +928,8 @@ encrypt_desc_data_and_base64(const hs_descriptor_t *desc,
ssize_t enc_b64_len, ret_len, enc_len;
char *encrypted_blob = NULL;
- enc_len = encrypt_descriptor_data(desc, encoded_str, &encrypted_blob,
+ enc_len = encrypt_descriptor_data(desc, secret_data, secret_data_len,
+ encoded_str, &encrypted_blob,
is_superencrypted_layer);
/* Get the encoded size plus a NUL terminating byte. */
enc_b64_len = base64_encode_size(enc_len, BASE64_ENCODE_MULTILINE) + 1;
@@ -922,9 +951,12 @@ encrypt_desc_data_and_base64(const hs_descriptor_t *desc,
* on success else a negative value. */
static int
encode_superencrypted_data(const hs_descriptor_t *desc,
+ const uint8_t *descriptor_cookie,
char **encrypted_blob_out)
{
int ret = -1;
+ uint8_t *secret_data = NULL;
+ size_t secret_data_len = 0;
char *layer2_str = NULL;
char *layer2_b64_ciphertext = NULL;
char *layer1_str = NULL;
@@ -944,8 +976,32 @@ encode_superencrypted_data(const hs_descriptor_t *desc,
goto err;
}
+ if (descriptor_cookie) {
+ /* If the descriptor cookie is present, we need both the blinded
+ * pubkey and the descriptor cookie as a secret data. */
+ secret_data_len = ED25519_PUBKEY_LEN + HS_DESC_DESCRIPTOR_COOKIE_LEN;
+ secret_data = tor_malloc(secret_data_len);
+
+ memcpy(secret_data,
+ desc->plaintext_data.blinded_pubkey.pubkey,
+ ED25519_PUBKEY_LEN);
+ memcpy(secret_data + ED25519_PUBKEY_LEN,
+ descriptor_cookie,
+ HS_DESC_DESCRIPTOR_COOKIE_LEN);
+ } else {
+ /* If the descriptor cookie is not present, we need only the blinded
+ * pubkey as a secret data. */
+ secret_data_len = ED25519_PUBKEY_LEN;
+ secret_data = tor_malloc(secret_data_len);
+ memcpy(secret_data,
+ desc->plaintext_data.blinded_pubkey.pubkey,
+ ED25519_PUBKEY_LEN);
+ }
+
/* Encrypt and b64 the inner layer */
- layer2_b64_ciphertext = encrypt_desc_data_and_base64(desc, layer2_str, 0);
+ layer2_b64_ciphertext =
+ encrypt_desc_data_and_base64(desc, secret_data, secret_data_len,
+ layer2_str, 0);
if (!layer2_b64_ciphertext) {
goto err;
}
@@ -957,7 +1013,11 @@ encode_superencrypted_data(const hs_descriptor_t *desc,
}
/* Encrypt and base64 the middle layer */
- layer1_b64_ciphertext = encrypt_desc_data_and_base64(desc, layer1_str, 1);
+ layer1_b64_ciphertext =
+ encrypt_desc_data_and_base64(desc,
+ desc->plaintext_data.blinded_pubkey.pubkey,
+ ED25519_PUBKEY_LEN,
+ layer1_str, 1);
if (!layer1_b64_ciphertext) {
goto err;
}
@@ -966,6 +1026,8 @@ encode_superencrypted_data(const hs_descriptor_t *desc,
ret = 0;
err:
+ memwipe(secret_data, 0, secret_data_len);
+ tor_free(secret_data);
tor_free(layer1_str);
tor_free(layer2_str);
tor_free(layer2_b64_ciphertext);
@@ -979,7 +1041,9 @@ encode_superencrypted_data(const hs_descriptor_t *desc,
* and encoded_out is untouched. */
static int
desc_encode_v3(const hs_descriptor_t *desc,
- const ed25519_keypair_t *signing_kp, char **encoded_out)
+ const ed25519_keypair_t *signing_kp,
+ const uint8_t *descriptor_cookie,
+ char **encoded_out)
{
int ret = -1;
char *encoded_str = NULL;
@@ -1027,7 +1091,8 @@ desc_encode_v3(const hs_descriptor_t *desc,
/* Build the superencrypted data section. */
{
char *enc_b64_blob=NULL;
- if (encode_superencrypted_data(desc, &enc_b64_blob) < 0) {
+ if (encode_superencrypted_data(desc, descriptor_cookie,
+ &enc_b64_blob) < 0) {
goto err;
}
smartlist_add_asprintf(lines,
@@ -1372,7 +1437,12 @@ decrypt_desc_layer,(const hs_descriptor_t *desc,
/* 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,
+ /* 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,
+ salt, HS_DESC_ENCRYPTED_SALT_LEN,
secret_key, sizeof(secret_key),
secret_iv, sizeof(secret_iv),
mac_key, sizeof(mac_key),
@@ -2344,6 +2414,7 @@ static int
(*encode_handlers[])(
const hs_descriptor_t *desc,
const ed25519_keypair_t *signing_kp,
+ const uint8_t *descriptor_cookie,
char **encoded_out) =
{
/* v0 */ NULL, /* v1 */ NULL, /* v2 */ NULL,
@@ -2351,14 +2422,20 @@ static int
};
/* Encode the given descriptor desc including signing with the given key pair
- * signing_kp. On success, encoded_out points to a newly allocated NUL
- * terminated string that contains the encoded descriptor as a string.
+ * signing_kp and encrypting with the given descriptor cookie.
+ *
+ * If the client authorization is enabled, descriptor_cookie must be the same
+ * as the one used to build hs_desc_authorized_client_t in the descriptor.
+ * Otherwise, it must be NULL. On success, encoded_out points to a newly
+ * allocated NUL terminated string that contains the encoded descriptor as
+ * a string.
*
* Return 0 on success and encoded_out is a valid pointer. On error, -1 is
* returned and encoded_out is set to NULL. */
MOCK_IMPL(int,
hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
const ed25519_keypair_t *signing_kp,
+ const uint8_t *descriptor_cookie,
char **encoded_out))
{
int ret = -1;
@@ -2377,16 +2454,20 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
tor_assert(ARRAY_LENGTH(encode_handlers) >= version);
tor_assert(encode_handlers[version]);
- ret = encode_handlers[version](desc, signing_kp, encoded_out);
+ ret = encode_handlers[version](desc, signing_kp,
+ descriptor_cookie, encoded_out);
if (ret < 0) {
goto err;
}
/* Try to decode what we just encoded. Symmetry is nice! */
- ret = hs_desc_decode_descriptor(*encoded_out, desc->subcredential, NULL);
- if (BUG(ret < 0)) {
- goto err;
- }
+ /* XXX: I need to disable this assertation for now to make the test pass.
+ * I will enable it again when I finish writing the decoding */
+ /* ret = hs_desc_decode_descriptor(*encoded_out, */
+ /* desc->subcredential, NULL); */
+ /* if (BUG(ret < 0)) { */
+ /* goto err; */
+ /* } */
return 0;
diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h
index 3e7dcc457..870016432 100644
--- a/src/feature/hs/hs_descriptor.h
+++ b/src/feature/hs/hs_descriptor.h
@@ -37,12 +37,6 @@ struct link_specifier_t;
#define HS_DESC_CERT_LIFETIME (54 * 60 * 60)
/* Length of the salt needed for the encrypted section of a descriptor. */
#define HS_DESC_ENCRYPTED_SALT_LEN 16
-/* Length of the secret input needed for the KDF construction which derives
- * the encryption key for the encrypted data section of the descriptor. This
- * adds up to 68 bytes being the blinded key, hashed subcredential and
- * revision counter. */
-#define HS_DESC_ENCRYPTED_SECRET_INPUT_LEN \
- ED25519_PUBKEY_LEN + DIGEST256_LEN + sizeof(uint64_t)
/* Length of the KDF output value which is the length of the secret key,
* the secret IV and MAC key length which is the length of H() output. */
#define HS_DESC_ENCRYPTED_KDF_OUTPUT_LEN \
@@ -278,6 +272,7 @@ void hs_descriptor_clear_intro_points(hs_descriptor_t *desc);
MOCK_DECL(int,
hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
const ed25519_keypair_t *signing_kp,
+ const uint8_t *descriptor_cookie,
char **encoded_out));
int hs_desc_decode_descriptor(const char *encoded,
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 0ffe0926d..c1dc12dde 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -108,6 +108,10 @@ static int load_client_keys(hs_service_t *service);
static void set_descriptor_revision_counter(hs_service_descriptor_t *hs_desc,
time_t now, bool is_current);
static void move_descriptors(hs_service_t *src, hs_service_t *dst);
+static int service_encode_descriptor(const hs_service_t *service,
+ const hs_service_descriptor_t *desc,
+ const ed25519_keypair_t *signing_kp,
+ char **encoded_out);
/* Helper: Function to compare two objects in the service map. Return 1 if the
* two service have the same master public identity key. */
@@ -1801,7 +1805,7 @@ build_service_descriptor(hs_service_t *service, time_t now,
/* Let's make sure that we've created a descriptor that can actually be
* encoded properly. This function also checks if the encoded output is
* decodable after. */
- if (BUG(hs_desc_encode_descriptor(desc->desc, &desc->signing_kp,
+ if (BUG(service_encode_descriptor(service, desc, &desc->signing_kp,
&encoded_desc) < 0)) {
goto err;
}
@@ -2640,7 +2644,7 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
/* First of all, we'll encode the descriptor. This should NEVER fail but
* just in case, let's make sure we have an actual usable descriptor. */
- if (BUG(hs_desc_encode_descriptor(desc->desc, &desc->signing_kp,
+ if (BUG(service_encode_descriptor(service, desc, &desc->signing_kp,
&encoded_desc) < 0)) {
goto end;
}
@@ -3206,6 +3210,34 @@ service_key_on_disk(const char *directory_path)
ed25519_keypair_free(kp);
tor_free(fname);
+
+ return ret;
+}
+
+/* This is a proxy function before actually calling hs_desc_encode_descriptor
+ * because we need some preprocessing here */
+static int
+service_encode_descriptor(const hs_service_t *service,
+ const hs_service_descriptor_t *desc,
+ const ed25519_keypair_t *signing_kp,
+ char **encoded_out)
+{
+ int ret;
+ const uint8_t *descriptor_cookie = NULL;
+
+ tor_assert(service);
+ tor_assert(desc);
+ tor_assert(encoded_out);
+
+ /* If the client authorization is enabled, send the descriptor cookie to
+ * hs_desc_encode_descriptor. Otherwise, send NULL */
+ if (service->config.is_client_auth_enabled) {
+ descriptor_cookie = desc->descriptor_cookie;
+ }
+
+ ret = hs_desc_encode_descriptor(desc->desc, signing_kp,
+ descriptor_cookie, encoded_out);
+
return ret;
}
@@ -3416,7 +3448,8 @@ hs_service_lookup_current_desc(const ed25519_public_key_t *pk)
/* No matter what is the result (which should never be a failure), return
* the encoded variable, if success it will contain the right thing else
* it will be NULL. */
- hs_desc_encode_descriptor(service->desc_current->desc,
+ service_encode_descriptor(service,
+ service->desc_current,
&service->desc_current->signing_kp,
&encoded_desc);
return encoded_desc;
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index afe3eafa2..bb2ba981f 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -98,8 +98,11 @@ static hs_descriptor_t *
hs_helper_build_hs_desc_impl(unsigned int no_ip,
const ed25519_keypair_t *signing_kp)
{
+ int ret;
+ int i;
time_t now = approx_time();
ed25519_keypair_t blinded_kp;
+ curve25519_keypair_t auth_ephemeral_kp;
hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc));
desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX;
@@ -126,6 +129,22 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
desc->subcredential);
+ /* Setup superencrypted data section. */
+ ret = curve25519_keypair_generate(&auth_ephemeral_kp, 0);
+ tt_int_op(ret, ==, 0);
+ memcpy(&desc->superencrypted_data.auth_ephemeral_pubkey,
+ &auth_ephemeral_kp.pubkey,
+ sizeof(curve25519_public_key_t));
+
+ desc->superencrypted_data.clients = smartlist_new();
+ for (i = 0; i < HS_DESC_AUTH_CLIENT_MULTIPLE; 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(desc->superencrypted_data.clients, desc_client);
+ }
+
/* Setup encrypted data section. */
desc->encrypted_data.create2_ntor = 1;
desc->encrypted_data.intro_auth_types = smartlist_new();
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index c1a69af82..33bb00e6e 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -64,7 +64,7 @@ test_directory(void *arg)
tt_int_op(ret, OP_EQ, 0);
desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1);
tt_assert(desc1);
- ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str);
+ ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str);
tt_int_op(ret, OP_EQ, 0);
/* Very first basic test, should be able to be stored, survive a
@@ -102,7 +102,7 @@ test_directory(void *arg)
desc_zero_lifetime->plaintext_data.lifetime_sec = 0;
char *desc_zero_lifetime_str;
ret = hs_desc_encode_descriptor(desc_zero_lifetime, &signing_kp_zero,
- &desc_zero_lifetime_str);
+ NULL, &desc_zero_lifetime_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_store_as_dir(desc1_str);
@@ -153,7 +153,7 @@ test_directory(void *arg)
tt_int_op(ret, OP_EQ, 1);
/* Bump revision counter. */
desc1->plaintext_data.revision_counter++;
- ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &new_desc_str);
+ ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &new_desc_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_store_as_dir(new_desc_str);
tt_int_op(ret, OP_EQ, 0);
@@ -187,7 +187,7 @@ test_clean_as_dir(void *arg)
tt_int_op(ret, OP_EQ, 0);
desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1);
tt_assert(desc1);
- ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str);
+ ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_store_as_dir(desc1_str);
tt_int_op(ret, OP_EQ, 0);
@@ -301,7 +301,7 @@ test_upload_and_download_hs_desc(void *arg)
published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
tt_assert(published_desc);
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
- &published_desc_str);
+ NULL, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
}
@@ -365,7 +365,7 @@ test_hsdir_revision_counter_check(void *arg)
published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
tt_assert(published_desc);
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
- &published_desc_str);
+ NULL, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
}
@@ -407,7 +407,7 @@ test_hsdir_revision_counter_check(void *arg)
published_desc->plaintext_data.revision_counter = 1313;
tor_free(published_desc_str);
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
- &published_desc_str);
+ NULL, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
@@ -482,7 +482,7 @@ test_client_cache(void *arg)
published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
tt_assert(published_desc);
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
- &published_desc_str);
+ NULL, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
memcpy(wanted_subcredential, published_desc->subcredential, DIGEST256_LEN);
tt_assert(!tor_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index 57da03ca2..e03c80098 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -366,7 +366,7 @@ test_client_pick_intro(void *arg)
{
char *encoded = NULL;
desc = hs_helper_build_hs_desc_with_ip(&service_kp);
- ret = hs_desc_encode_descriptor(desc, &service_kp, &encoded);
+ ret = hs_desc_encode_descriptor(desc, &service_kp, NULL, &encoded);
tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index c1001ee5c..c60d6e264 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -428,11 +428,13 @@ mock_directory_initiate_request(directory_request_t *req)
static int
mock_hs_desc_encode_descriptor(const hs_descriptor_t *desc,
- const ed25519_keypair_t *signing_kp,
- char **encoded_out)
+ const ed25519_keypair_t *signing_kp,
+ const uint8_t *descriptor_cookie,
+ char **encoded_out)
{
(void)desc;
(void)signing_kp;
+ (void)descriptor_cookie;
tor_asprintf(encoded_out, "lulu");
return 0;
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index 9191b74d9..bc72b34c0 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -300,7 +300,7 @@ test_encode_descriptor(void *arg)
ret = ed25519_keypair_generate(&signing_kp, 0);
tt_int_op(ret, OP_EQ, 0);
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
- ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded);
+ ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
@@ -333,7 +333,7 @@ test_decode_descriptor(void *arg)
ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential, &decoded);
tt_int_op(ret, OP_EQ, -1);
- ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded);
+ ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded);
tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
@@ -353,7 +353,8 @@ test_decode_descriptor(void *arg)
desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip);
tt_assert(desc_no_ip);
tor_free(encoded);
- ret = hs_desc_encode_descriptor(desc_no_ip, &signing_kp_no_ip, &encoded);
+ ret = hs_desc_encode_descriptor(desc_no_ip, &signing_kp_no_ip,
+ NULL, &encoded);
tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
hs_descriptor_free(decoded);
1
0

[tor/master] hs-v3: Load client authorization secret key from file
by nickm@torproject.org 07 Sep '18
by nickm@torproject.org 07 Sep '18
07 Sep '18
commit 8e81fcd51ae9b9b373f0254381728a8f4d93236d
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Sun Aug 19 08:22:13 2018 +0700
hs-v3: Load client authorization secret key from file
The new ClientOnionAuthDir option is introduced which is where tor looks to
find the HS v3 client authorization files containing the client private key
material.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/app/config/config.c | 7 +-
src/app/config/or_options_st.h | 2 +
src/feature/hs/hs_client.c | 215 +++++++++++++++++++++++++++++++++++++++++
src/feature/hs/hs_client.h | 13 +++
src/feature/hs/hs_config.c | 27 ++++++
src/feature/hs/hs_config.h | 1 +
6 files changed, 263 insertions(+), 2 deletions(-)
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 339f8e247..ce9ae8d7c 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -450,6 +450,7 @@ static config_var_t option_vars_[] = {
VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
V(HidServAuth, LINELIST, NULL),
+ V(ClientOnionAuthDir, FILENAME, NULL),
OBSOLETE("CloseHSClientCircuitsImmediatelyOnTimeout"),
OBSOLETE("CloseHSServiceRendCircuitsImmediatelyOnTimeout"),
V(HiddenServiceSingleHopMode, BOOL, "0"),
@@ -1917,7 +1918,7 @@ options_act(const or_options_t *old_options)
// LCOV_EXCL_STOP
}
- if (running_tor && rend_parse_service_authorization(options, 0) < 0) {
+ if (running_tor && hs_config_client_auth_all(options, 0) < 0) {
// LCOV_EXCL_START
log_warn(LD_BUG, "Previously validated client authorization for "
"hidden services could not be added!");
@@ -3188,6 +3189,8 @@ warn_about_relative_paths(or_options_t *options)
n += warn_if_option_path_is_relative("AccelDir",options->AccelDir);
n += warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
n += warn_if_option_path_is_relative("PidFile",options->PidFile);
+ n += warn_if_option_path_is_relative("ClientOnionAuthDir",
+ options->ClientOnionAuthDir);
for (config_line_t *hs_line = options->RendConfigLines; hs_line;
hs_line = hs_line->next) {
@@ -4339,7 +4342,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Failed to configure rendezvous options. See logs for details.");
/* Parse client-side authorization for hidden services. */
- if (rend_parse_service_authorization(options, 1) < 0)
+ if (hs_config_client_auth_all(options, 1) < 0)
REJECT("Failed to configure client authorization for hidden services. "
"See logs for details.");
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index 8ef01f80e..f6d796638 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -380,6 +380,8 @@ struct or_options_t {
struct config_line_t *HidServAuth; /**< List of configuration lines for
* client-side authorizations for hidden
* services */
+ char *ClientOnionAuthDir; /**< Directory to keep client
+ * onion service authorization secret keys */
char *ContactInfo; /**< Contact info to be published in the directory. */
int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 1f9218e15..7c545c35d 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -42,6 +42,10 @@
#include "core/or/extend_info_st.h"
#include "core/or/origin_circuit_st.h"
+/* Client-side authorizations for hidden services; map of service identity
+ * public key to hs_client_service_authorization_t *. */
+static digest256map_t *client_auths = NULL;
+
/* Return a human-readable string for the client fetch status code. */
static const char *
fetch_status_to_string(hs_client_fetch_status_t status)
@@ -1393,6 +1397,216 @@ hs_client_receive_rendezvous_acked(origin_circuit_t *circ,
return -1;
}
+#define client_service_authorization_free(auth) \
+ FREE_AND_NULL(hs_client_service_authorization_t, \
+ client_service_authorization_free_, (auth))
+
+static void
+client_service_authorization_free_(hs_client_service_authorization_t *auth)
+{
+ if (auth) {
+ memwipe(auth, 0, sizeof(*auth));
+ }
+ tor_free(auth);
+}
+
+/** Helper for digest256map_free. */
+static void
+client_service_authorization_free_void(void *auth)
+{
+ client_service_authorization_free_(auth);
+}
+
+static void
+client_service_authorization_free_all(void)
+{
+ if (!client_auths) {
+ return;
+ }
+ digest256map_free(client_auths, client_service_authorization_free_void);
+}
+
+/* Check if the auth key file name is valid or not. Return 1 if valid,
+ * otherwise return 0. */
+static int
+auth_key_filename_is_valid(const char *filename)
+{
+ int ret = 1;
+ const char *valid_extension = ".auth_private";
+
+ tor_assert(filename);
+
+ /* The length of the filename must be greater than the length of the
+ * extension and the valid extension must be at the end of filename. */
+ if (!strcmpend(filename, valid_extension) &&
+ strlen(filename) != strlen(valid_extension)) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static hs_client_service_authorization_t *
+parse_auth_file_content(const char *client_key_str)
+{
+ char *onion_address = NULL;
+ char *auth_type = NULL;
+ char *key_type = NULL;
+ char *seckey_b32 = NULL;
+ hs_client_service_authorization_t *auth = NULL;
+ smartlist_t *fields = smartlist_new();
+
+ tor_assert(client_key_str);
+
+ smartlist_split_string(fields, client_key_str, ":",
+ SPLIT_SKIP_SPACE, 0);
+ /* Wrong number of fields. */
+ if (smartlist_len(fields) != 4) {
+ goto err;
+ }
+
+ onion_address = smartlist_get(fields, 0);
+ auth_type = smartlist_get(fields, 1);
+ key_type = smartlist_get(fields, 2);
+ seckey_b32 = smartlist_get(fields, 3);
+
+ /* Currently, the only supported auth type is "descriptor" and the only
+ * supported key type is "x25519". */
+ if (strcmp(auth_type, "descriptor") || strcmp(key_type, "x25519")) {
+ goto err;
+ }
+
+ auth = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
+ if (base32_decode((char *) auth->enc_seckey.secret_key,
+ sizeof(auth->enc_seckey.secret_key),
+ seckey_b32, strlen(seckey_b32)) < 0) {
+ goto err;
+ }
+ strncpy(auth->onion_address, onion_address, HS_SERVICE_ADDR_LEN_BASE32);
+
+ /* Success. */
+ goto done;
+
+ err:
+ client_service_authorization_free(auth);
+ done:
+ /* It is also a good idea to wipe the private key. */
+ if (seckey_b32) {
+ memwipe(seckey_b32, 0, strlen(seckey_b32));
+ }
+ if (fields) {
+ SMARTLIST_FOREACH(fields, char *, s, tor_free(s));
+ smartlist_free(fields);
+ }
+ return auth;
+}
+
+/* From a set of <b>options</b>, setup every client authorization detail
+ * found. Return 0 on success or -1 on failure. If <b>validate_only</b>
+ * is set, parse, warn and return as normal, but don't actually change
+ * the configuration. */
+int
+hs_config_client_authorization(const or_options_t *options,
+ int validate_only)
+{
+ int ret = -1;
+ digest256map_t *auths = digest256map_new();
+ char *key_dir = NULL;
+ smartlist_t *file_list = NULL;
+ char *client_key_str = NULL;
+ char *client_key_file_path = NULL;
+
+ tor_assert(options);
+
+ /* There is no client auth configured. We can just silently ignore this
+ * function. */
+ if (!options->ClientOnionAuthDir) {
+ ret = 0;
+ goto end;
+ }
+
+ key_dir = tor_strdup(options->ClientOnionAuthDir);
+
+ /* Make sure the directory exists and is private enough. */
+ if (check_private_dir(key_dir, 0, options->User) < 0) {
+ goto end;
+ }
+
+ file_list = tor_listdir(key_dir);
+ if (file_list == NULL) {
+ log_warn(LD_REND, "Client authorization key directory %s can't be listed.",
+ key_dir);
+ goto end;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(file_list, char *, filename) {
+
+ hs_client_service_authorization_t *auth = NULL;
+ ed25519_public_key_t identity_pk;
+
+ if (auth_key_filename_is_valid(filename)) {
+ /* Create a full path for a file. */
+ client_key_file_path = hs_path_from_filename(key_dir, filename);
+ client_key_str = read_file_to_str(client_key_file_path, 0, NULL);
+ /* Free the file path immediately after using it. */
+ tor_free(client_key_file_path);
+
+ /* If we cannot read the file, continue with the next file. */
+ if (!client_key_str) {
+ continue;
+ }
+
+ auth = parse_auth_file_content(client_key_str);
+ /* Free immediately after using it. */
+ tor_free(client_key_str);
+
+ if (auth) {
+ /* Parse the onion address to get an identity public key and use it
+ * as a key of global map in the future. */
+ if (hs_parse_address(auth->onion_address, &identity_pk,
+ NULL, NULL) < 0) {
+ client_service_authorization_free(auth);
+ continue;
+ }
+
+ if (digest256map_get(auths, identity_pk.pubkey)) {
+ client_service_authorization_free(auth);
+
+ log_warn(LD_REND, "Duplicate authorization for the same hidden "
+ "service.");
+ goto end;
+ }
+
+ digest256map_set(auths, identity_pk.pubkey, auth);
+ }
+ }
+
+ } SMARTLIST_FOREACH_END(filename);
+
+ /* Success. */
+ ret = 0;
+
+ end:
+ tor_free(key_dir);
+ tor_free(client_key_str);
+ tor_free(client_key_file_path);
+ if (file_list) {
+ SMARTLIST_FOREACH(file_list, char *, s, tor_free(s));
+ smartlist_free(file_list);
+ }
+
+ if (!validate_only && ret == 0) {
+ client_service_authorization_free_all();
+ client_auths = auths;
+ } else {
+ digest256map_free(auths, client_service_authorization_free_void);
+ }
+
+ return ret;
+}
+
/* This is called when a descriptor has arrived following a fetch request and
* has been stored in the client cache. Every entry connection that matches
* the service identity key in the ident will get attached to the hidden
@@ -1589,6 +1803,7 @@ hs_client_free_all(void)
{
/* Purge the hidden service request cache. */
hs_purge_last_hid_serv_requests();
+ client_service_authorization_free_all();
}
/* Purge all potentially remotely-detectable state held in the hidden
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index 6ee9f40c0..6d4c84774 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -31,6 +31,16 @@ typedef enum {
HS_CLIENT_FETCH_PENDING = 5,
} hs_client_fetch_status_t;
+/** Client-side configuration of authorization for a service. */
+typedef struct hs_client_service_authorization_t {
+ /* An curve25519 secret key used to compute decryption keys that
+ * allow the client to decrypt the hidden service descriptor. */
+ curve25519_secret_key_t enc_seckey;
+
+ /* An onion address that is used to connect to the onion service. */
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1];
+} hs_client_service_authorization_t;
+
void hs_client_note_connection_attempt_succeeded(
const edge_connection_t *conn);
@@ -63,6 +73,9 @@ void hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident);
extend_info_t *hs_client_get_random_intro_from_edge(
const edge_connection_t *edge_conn);
+int hs_config_client_authorization(const or_options_t *options,
+ int validate_only);
+
int hs_client_reextend_intro_circuit(origin_circuit_t *circ);
void hs_client_purge_state(void);
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index e97257648..eaeb58829 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -27,7 +27,9 @@
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
+#include "feature/hs/hs_client.h"
#include "feature/hs/hs_service.h"
+#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
#include "lib/encoding/confline.h"
#include "app/config/or_options_st.h"
@@ -613,3 +615,28 @@ hs_config_service_all(const or_options_t *options, int validate_only)
/* Tor main should call the free all function on error. */
return ret;
}
+
+/* From a set of <b>options</b>, setup every client authorization found.
+ * Return 0 on success or -1 on failure. If <b>validate_only</b> is set,
+ * parse, warn and return as normal, but don't actually change the
+ * configured state. */
+int
+hs_config_client_auth_all(const or_options_t *options, int validate_only)
+{
+ int ret = -1;
+
+ /* Configure v2 authorization. */
+ if (rend_parse_service_authorization(options, validate_only) < 0) {
+ goto done;
+ }
+
+ /* Configure v3 authorization. */
+ if (hs_config_client_authorization(options, validate_only) < 0) {
+ goto done;
+ }
+
+ /* Success. */
+ ret = 0;
+ done:
+ return ret;
+}
diff --git a/src/feature/hs/hs_config.h b/src/feature/hs/hs_config.h
index 96eb19ee7..f443e814c 100644
--- a/src/feature/hs/hs_config.h
+++ b/src/feature/hs/hs_config.h
@@ -19,6 +19,7 @@
/* API */
int hs_config_service_all(const or_options_t *options, int validate_only);
+int hs_config_client_auth_all(const or_options_t *options, int validate_only);
#endif /* !defined(TOR_HS_CONFIG_H) */
1
0

07 Sep '18
commit 63576b01663f1af0ee2b7bd29dd840d121103315
Author: Suphanat Chunhapanya <haxx.pop(a)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(a)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
};
1
0

07 Sep '18
commit 9c362192361bec379f83f37426236b41c2a17e8e
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Sun Aug 19 08:26:43 2018 +0700
test: HS v3 client authorization loading secret key
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/feature/hs/hs_client.c | 14 +++-
src/feature/hs/hs_client.h | 11 +++
src/test/test_hs_client.c | 174 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 197 insertions(+), 2 deletions(-)
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 7c545c35d..18c79e0c4 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -1428,7 +1428,7 @@ client_service_authorization_free_all(void)
/* Check if the auth key file name is valid or not. Return 1 if valid,
* otherwise return 0. */
-static int
+STATIC int
auth_key_filename_is_valid(const char *filename)
{
int ret = 1;
@@ -1448,7 +1448,7 @@ auth_key_filename_is_valid(const char *filename)
return ret;
}
-static hs_client_service_authorization_t *
+STATIC hs_client_service_authorization_t *
parse_auth_file_content(const char *client_key_str)
{
char *onion_address = NULL;
@@ -1836,3 +1836,13 @@ hs_client_dir_info_changed(void)
* AP_CONN_STATE_RENDDESC_WAIT state in order to fetch the descriptor. */
retry_all_socks_conn_waiting_for_desc();
}
+
+#ifdef TOR_UNIT_TESTS
+
+STATIC digest256map_t *
+get_hs_client_auths_map(void)
+{
+ return client_auths;
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index 6d4c84774..1ba0338dc 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -84,6 +84,11 @@ void hs_client_free_all(void);
#ifdef HS_CLIENT_PRIVATE
+STATIC int auth_key_filename_is_valid(const char *filename);
+
+STATIC hs_client_service_authorization_t *
+parse_auth_file_content(const char *client_key_str);
+
STATIC routerstatus_t *
pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk);
@@ -99,6 +104,12 @@ STATIC int handle_rendezvous2(origin_circuit_t *circ, const uint8_t *payload,
MOCK_DECL(STATIC hs_client_fetch_status_t,
fetch_v3_desc, (const ed25519_public_key_t *onion_identity_pk));
+#ifdef TOR_UNIT_TESTS
+
+STATIC digest256map_t *get_hs_client_auths_map(void);
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
#endif /* defined(HS_CLIENT_PRIVATE) */
#endif /* !defined(TOR_HS_CLIENT_H) */
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index e03c80098..eacbd328e 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -6,6 +6,7 @@
* \brief Test prop224 HS client functionality.
*/
+#define CONFIG_PRIVATE
#define CRYPTO_PRIVATE
#define MAIN_PRIVATE
#define HS_CLIENT_PRIVATE
@@ -32,6 +33,7 @@
#include "feature/hs/hs_circuit.h"
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_client.h"
+#include "feature/hs/hs_config.h"
#include "feature/hs/hs_ident.h"
#include "feature/hs/hs_cache.h"
#include "core/or/circuitlist.h"
@@ -73,6 +75,20 @@ mock_networkstatus_get_live_consensus(time_t now)
return &mock_ns;
}
+static int
+helper_config_client(const char *conf, int validate_only)
+{
+ int ret = 0;
+ or_options_t *options = NULL;
+ tt_assert(conf);
+ options = helper_parse_options(conf);
+ tt_assert(options);
+ ret = hs_config_client_auth_all(options, validate_only);
+ done:
+ or_options_free(options);
+ return ret;
+}
+
/* Test helper function: Setup a circuit and a stream with the same hidden
* service destination, and put them in <b>circ_out</b> and
* <b>conn_out</b>. Make the stream wait for circuits to be established to the
@@ -601,6 +617,158 @@ test_descriptor_fetch(void *arg)
hs_free_all();
}
+static void
+test_auth_key_filename_is_valid(void *arg)
+{
+ (void) arg;
+
+ /* Valid file name. */
+ tt_assert(auth_key_filename_is_valid("a.auth_private"));
+ /* Valid file name with special character. */
+ tt_assert(auth_key_filename_is_valid("a-.auth_private"));
+ /* Invalid extension. */
+ tt_assert(!auth_key_filename_is_valid("a.ath_private"));
+ /* Nothing before the extension. */
+ tt_assert(!auth_key_filename_is_valid(".auth_private"));
+
+ done:
+ ;
+}
+
+static void
+test_parse_auth_file_content(void *arg)
+{
+ hs_client_service_authorization_t *auth = NULL;
+
+ (void) arg;
+
+ /* Valid authorized client. */
+ auth = parse_auth_file_content(
+ "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
+ "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
+ tt_assert(auth);
+
+ /* Wrong number of fields. */
+ tt_assert(!parse_auth_file_content("a:b"));
+ /* Wrong auth type. */
+ tt_assert(!parse_auth_file_content(
+ "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:x:"
+ "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq"));
+ /* Wrong key type. */
+ tt_assert(!parse_auth_file_content(
+ "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
+ "x:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq"));
+ /* Some malformed string. */
+ tt_assert(!parse_auth_file_content("xx:descriptor:x25519:aa=="));
+
+ done:
+ tor_free(auth);
+}
+
+static char *
+mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out)
+{
+ char *ret = NULL;
+
+ (void) flags;
+ (void) stat_out;
+
+ if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR
+ "client1.auth_private"))) {
+ ret = tor_strdup(
+ "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
+ "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
+ goto done;
+ }
+
+ if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR "dummy.xxx"))) {
+ ret = tor_strdup(
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:descriptor:"
+ "x25519:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ goto done;
+ }
+
+ if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR
+ "client2.auth_private"))) {
+ ret = tor_strdup(
+ "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid:descriptor:"
+ "x25519:fdreqzjqso7d2ac7qscrxfl5qfpamdvgy5d6cxejcgzc3hvhurmq");
+ goto done;
+ }
+
+ done:
+ return ret;
+}
+
+static int
+mock_check_private_dir(const char *dirname, cpd_check_t check,
+ const char *effective_user)
+{
+ (void) dirname;
+ (void) check;
+ (void) effective_user;
+
+ return 0;
+}
+
+static smartlist_t *
+mock_tor_listdir(const char *dirname)
+{
+ smartlist_t *file_list = smartlist_new();
+
+ (void) dirname;
+
+ smartlist_add(file_list, tor_strdup("client1.auth_private"));
+ smartlist_add(file_list, tor_strdup("dummy.xxx"));
+ smartlist_add(file_list, tor_strdup("client2.auth_private"));
+
+ return file_list;
+}
+
+static void
+test_config_client_authorization(void *arg)
+{
+ int ret;
+ char *conf = NULL;
+ ed25519_public_key_t pk1, pk2;
+ digest256map_t *global_map = NULL;
+ char *key_dir = tor_strdup(get_fname("auth_keys"));
+
+ (void) arg;
+
+ MOCK(read_file_to_str, mock_read_file_to_str);
+ MOCK(tor_listdir, mock_tor_listdir);
+ MOCK(check_private_dir, mock_check_private_dir);
+
+#define conf_fmt \
+ "ClientOnionAuthDir %s\n"
+
+ tor_asprintf(&conf, conf_fmt, key_dir);
+ ret = helper_config_client(conf, 0);
+ tor_free(conf);
+ tt_int_op(ret, OP_EQ, 0);
+
+#undef conf_fmt
+
+ global_map = get_hs_client_auths_map();
+ tt_int_op(digest256map_size(global_map), OP_EQ, 2);
+
+ hs_parse_address("4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad",
+ &pk1, NULL, NULL);
+ hs_parse_address("25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid",
+ &pk2, NULL, NULL);
+
+ tt_assert(digest256map_get(global_map, pk1.pubkey));
+ tt_assert(digest256map_get(global_map, pk2.pubkey));
+
+ done:
+ tor_free(key_dir);
+ hs_free_all();
+ UNMOCK(read_file_to_str);
+ UNMOCK(tor_listdir);
+ UNMOCK(check_private_dir);
+}
+
struct testcase_t hs_client_tests[] = {
{ "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
TT_FORK, NULL, NULL },
@@ -610,5 +778,11 @@ struct testcase_t hs_client_tests[] = {
TT_FORK, NULL, NULL },
{ "descriptor_fetch", test_descriptor_fetch,
TT_FORK, NULL, NULL },
+ { "auth_key_filename_is_valid", test_auth_key_filename_is_valid, TT_FORK,
+ NULL, NULL },
+ { "parse_auth_file_content", test_parse_auth_file_content, TT_FORK,
+ NULL, NULL },
+ { "config_client_authorization", test_config_client_authorization,
+ TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
1
0

[tor/master] hs-v3: Decrypt the descriptor with client private key
by nickm@torproject.org 07 Sep '18
by nickm@torproject.org 07 Sep '18
07 Sep '18
commit 7acb720027dd702332fb5a539683742801443f00
Author: Suphanat Chunhapanya <haxx.pop(a)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(a)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);
1
0

[tor/master] test: HS v3 descriptor decoding with client authorization
by nickm@torproject.org 07 Sep '18
by nickm@torproject.org 07 Sep '18
07 Sep '18
commit 69fb25b0f6f3c2e7397b3f5e49213025ab1e8173
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Sun Apr 22 01:13:50 2018 +0700
test: HS v3 descriptor decoding with client authorization
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/test/hs_test_helpers.c | 26 ++++++++++++++++
src/test/test_hs_descriptor.c | 70 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index bb2ba981f..f8c582afc 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -226,6 +226,32 @@ hs_helper_desc_equal(const hs_descriptor_t *desc1,
* encrypted blob. As contrast to the decoding process where we populate a
* descriptor object. */
+ /* Superencrypted data section. */
+ tt_mem_op(desc1->superencrypted_data.auth_ephemeral_pubkey.public_key, OP_EQ,
+ desc2->superencrypted_data.auth_ephemeral_pubkey.public_key,
+ CURVE25519_PUBKEY_LEN);
+
+ /* Auth clients. */
+ {
+ tt_assert(desc1->superencrypted_data.clients);
+ tt_assert(desc2->superencrypted_data.clients);
+ tt_int_op(smartlist_len(desc1->superencrypted_data.clients), ==,
+ smartlist_len(desc2->superencrypted_data.clients));
+ for (int i=0;
+ i < smartlist_len(desc1->superencrypted_data.clients);
+ i++) {
+ hs_desc_authorized_client_t
+ *client1 = smartlist_get(desc1->superencrypted_data.clients, i),
+ *client2 = smartlist_get(desc2->superencrypted_data.clients, i);
+ tor_memeq(client1->client_id, client2->client_id,
+ sizeof(client1->client_id));
+ tor_memeq(client1->iv, client2->iv,
+ sizeof(client1->iv));
+ tor_memeq(client1->encrypted_cookie, client2->encrypted_cookie,
+ sizeof(client1->encrypted_cookie));
+ }
+ }
+
/* Encrypted data section. */
tt_uint_op(desc1->encrypted_data.create2_ntor, ==,
desc2->encrypted_data.create2_ntor);
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index e003ea5ff..952499a2c 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -330,6 +330,7 @@ static void
test_decode_descriptor(void *arg)
{
int ret;
+ int i;
char *encoded = NULL;
ed25519_keypair_t signing_kp;
hs_descriptor_t *desc = NULL;
@@ -381,6 +382,75 @@ test_decode_descriptor(void *arg)
tt_assert(decoded);
}
+ /* Decode a descriptor with auth clients. */
+ {
+ uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
+ curve25519_keypair_t auth_ephemeral_kp;
+ curve25519_keypair_t client_kp, invalid_client_kp;
+ smartlist_t *clients;
+ hs_desc_authorized_client_t *client, *fake_client;
+ client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
+
+ /* Prepare all the keys needed to build the auth client. */
+ curve25519_keypair_generate(&auth_ephemeral_kp, 0);
+ curve25519_keypair_generate(&client_kp, 0);
+ curve25519_keypair_generate(&invalid_client_kp, 0);
+ crypto_strongest_rand(descriptor_cookie, HS_DESC_DESCRIPTOR_COOKIE_LEN);
+
+ memcpy(&desc->superencrypted_data.auth_ephemeral_pubkey,
+ &auth_ephemeral_kp.pubkey, CURVE25519_PUBKEY_LEN);
+
+ /* Build and add the auth client to the descriptor. */
+ clients = desc->superencrypted_data.clients;
+ if (!clients) {
+ clients = smartlist_new();
+ }
+ hs_desc_build_authorized_client(&client_kp.pubkey,
+ &auth_ephemeral_kp.seckey,
+ descriptor_cookie, client);
+ smartlist_add(clients, client);
+
+ /* We need to add fake auth clients here. */
+ for (i=0; i < 15; ++i) {
+ fake_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
+ hs_desc_build_fake_authorized_client(fake_client);
+ smartlist_add(clients, fake_client);
+ }
+ desc->superencrypted_data.clients = clients;
+
+ /* Test the encoding/decoding in the following lines. */
+ hs_helper_get_subcred_from_identity_keypair(&signing_kp,
+ subcredential);
+ tor_free(encoded);
+ ret = hs_desc_encode_descriptor(desc, &signing_kp,
+ descriptor_cookie, &encoded);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(encoded);
+
+ /* If we do not have the client secret key, the decoding must fail. */
+ hs_descriptor_free(decoded);
+ ret = hs_desc_decode_descriptor(encoded, subcredential,
+ NULL, &decoded);
+ tt_int_op(ret, OP_LT, 0);
+ tt_assert(!decoded);
+
+ /* If we have an invalid client secret key, the decoding must fail. */
+ hs_descriptor_free(decoded);
+ ret = hs_desc_decode_descriptor(encoded, subcredential,
+ &invalid_client_kp.seckey, &decoded);
+ tt_int_op(ret, OP_LT, 0);
+ tt_assert(!decoded);
+
+ /* If we have the client secret key, the decoding must succeed and the
+ * decoded descriptor must be correct. */
+ ret = hs_desc_decode_descriptor(encoded, subcredential,
+ &client_kp.seckey, &decoded);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(decoded);
+
+ hs_helper_desc_equal(desc, decoded);
+ }
+
done:
hs_descriptor_free(desc);
hs_descriptor_free(desc_no_ip);
1
0

[tor/master] hs-v3: Re-enable the decoding in the encoding function
by nickm@torproject.org 07 Sep '18
by nickm@torproject.org 07 Sep '18
07 Sep '18
commit 53dd1699baf5cb09086644eaca239596aedbde15
Author: Suphanat Chunhapanya <haxx.pop(a)gmail.com>
Date: Sun Apr 22 20:51:21 2018 +0700
hs-v3: Re-enable the decoding in the encoding function
Previously, the validation by decoding a created descriptor was disabled
because the interface had to be entirely changed and not implemented at the
time.
This commit re-enabled it because it is now implemented.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/feature/hs/hs_descriptor.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 3f9b505cb..52b080271 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -2705,14 +2705,16 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
goto err;
}
- /* Try to decode what we just encoded. Symmetry is nice! */
- /* XXX: I need to disable this assertation for now to make the test pass.
- * I will enable it again when I finish writing the decoding */
- /* ret = hs_desc_decode_descriptor(*encoded_out, */
- /* desc->subcredential, NULL); */
- /* if (BUG(ret < 0)) { */
- /* goto err; */
- /* } */
+ /* Try to decode what we just encoded. Symmetry is nice!, but it is
+ * symmetric only if the client auth is disabled. That is, the descriptor
+ * cookie will be NULL. */
+ if (!descriptor_cookie) {
+ ret = hs_desc_decode_descriptor(*encoded_out, desc->subcredential,
+ NULL, NULL);
+ if (BUG(ret < 0)) {
+ goto err;
+ }
+ }
return 0;
1
0