aboutsummaryrefslogtreecommitdiff
path: root/src/feature/hs/hs_cell.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/hs/hs_cell.c')
-rw-r--r--src/feature/hs/hs_cell.c301
1 files changed, 266 insertions, 35 deletions
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index f84407de9e..0039825f3c 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -14,12 +14,14 @@
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_ob.h"
#include "core/crypto/hs_ntor.h"
+#include "core/or/congestion_control_common.h"
#include "core/or/origin_circuit_st.h"
/* Trunnel. */
+#include "trunnel/congestion_control.h"
#include "trunnel/ed25519_cert.h"
-#include "trunnel/hs/cell_common.h"
+#include "trunnel/extension.h"
#include "trunnel/hs/cell_establish_intro.h"
#include "trunnel/hs/cell_introduce1.h"
#include "trunnel/hs/cell_rendezvous.h"
@@ -372,6 +374,100 @@ introduce1_encrypt_and_encode(trn_cell_introduce1_t *cell,
tor_free(encrypted);
}
+/** Build the PoW cell extension and put it in the given extensions object.
+ * Return 0 on success, -1 on failure. */
+static int
+build_introduce_pow_extension(const hs_pow_solution_t *pow_solution,
+ trn_extension_t *extensions)
+{
+ ssize_t ret;
+ size_t pow_ext_encoded_len;
+ uint8_t *field_array;
+ trn_extension_field_t *field = NULL;
+ trn_cell_extension_pow_t *pow_ext = NULL;
+
+ tor_assert(pow_solution);
+ tor_assert(extensions);
+
+ /* We are creating a cell extension field of type PoW solution. */
+ field = trn_extension_field_new();
+ trn_extension_field_set_field_type(field, TRUNNEL_EXT_TYPE_POW);
+
+ /* Build PoW extension field. */
+ pow_ext = trn_cell_extension_pow_new();
+
+ /* Copy PoW solution values into PoW extension cell. */
+
+ /* Equi-X base scheme */
+ trn_cell_extension_pow_set_pow_version(pow_ext, TRUNNEL_POW_VERSION_EQUIX);
+
+ memcpy(trn_cell_extension_pow_getarray_pow_nonce(pow_ext),
+ &pow_solution->nonce, TRUNNEL_POW_NONCE_LEN);
+
+ trn_cell_extension_pow_set_pow_effort(pow_ext, pow_solution->effort);
+
+ memcpy(trn_cell_extension_pow_getarray_pow_seed(pow_ext),
+ pow_solution->seed_head, TRUNNEL_POW_SEED_HEAD_LEN);
+ memcpy(trn_cell_extension_pow_getarray_pow_solution(pow_ext),
+ pow_solution->equix_solution, TRUNNEL_POW_SOLUTION_LEN);
+
+ /* Set the field with the encoded PoW extension. */
+ ret = trn_cell_extension_pow_encoded_len(pow_ext);
+ if (BUG(ret <= 0)) {
+ goto err;
+ }
+ pow_ext_encoded_len = ret;
+
+ /* Set length field and the field array size length. */
+ trn_extension_field_set_field_len(field, pow_ext_encoded_len);
+ trn_extension_field_setlen_field(field, pow_ext_encoded_len);
+ /* Encode the PoW extension into the cell extension field. */
+ field_array = trn_extension_field_getarray_field(field);
+ ret = trn_cell_extension_pow_encode(field_array,
+ trn_extension_field_getlen_field(field), pow_ext);
+ if (BUG(ret <= 0)) {
+ goto err;
+ }
+ tor_assert(ret == (ssize_t)pow_ext_encoded_len);
+
+ /* Finally, encode field into the cell extension. */
+ trn_extension_add_fields(extensions, field);
+
+ /* We've just add an extension field to the cell extensions so increment the
+ * total number. */
+ trn_extension_set_num(extensions, trn_extension_get_num(extensions) + 1);
+
+ /* Cleanup. PoW extension has been encoded at this point. */
+ trn_cell_extension_pow_free(pow_ext);
+
+ return 0;
+
+err:
+ trn_extension_field_free(field);
+ trn_cell_extension_pow_free(pow_ext);
+ return -1;
+}
+
+/** Build and set the INTRODUCE congestion control extension in the given
+ * extensions. */
+static void
+build_introduce_cc_extension(trn_extension_t *extensions)
+{
+ trn_extension_field_t *field = NULL;
+
+ /* Build CC request extension. */
+ field = trn_extension_field_new();
+ trn_extension_field_set_field_type(field,
+ TRUNNEL_EXT_TYPE_CC_REQUEST);
+
+ /* No payload indicating a request to use congestion control. */
+ trn_extension_field_set_field_len(field, 0);
+
+ /* Build final extension. */
+ trn_extension_add_fields(extensions, field);
+ trn_extension_set_num(extensions, trn_extension_get_num(extensions) + 1);
+}
+
/** Using the INTRODUCE1 data, setup the ENCRYPTED section in cell. This means
* set it, encrypt it and encode it. */
static void
@@ -379,7 +475,7 @@ introduce1_set_encrypted(trn_cell_introduce1_t *cell,
const hs_cell_introduce1_data_t *data)
{
trn_cell_introduce_encrypted_t *enc_cell;
- trn_cell_extension_t *ext;
+ trn_extension_t *ext;
tor_assert(cell);
tor_assert(data);
@@ -387,10 +483,17 @@ introduce1_set_encrypted(trn_cell_introduce1_t *cell,
enc_cell = trn_cell_introduce_encrypted_new();
tor_assert(enc_cell);
- /* Set extension data. None are used. */
- ext = trn_cell_extension_new();
+ /* Setup extension(s) if any. */
+ ext = trn_extension_new();
tor_assert(ext);
- trn_cell_extension_set_num(ext, 0);
+ /* Build congestion control extension if enabled. */
+ if (data->cc_enabled) {
+ build_introduce_cc_extension(ext);
+ }
+ /* Build PoW extension if present. */
+ if (data->pow_solution) {
+ build_introduce_pow_extension(data->pow_solution, ext);
+ }
trn_cell_introduce_encrypted_set_extensions(enc_cell, ext);
/* Set the rendezvous cookie. */
@@ -454,20 +557,20 @@ build_establish_intro_dos_param(trn_cell_extension_dos_t *dos_ext,
* possible if there is a bug.) */
static int
build_establish_intro_dos_extension(const hs_service_config_t *service_config,
- trn_cell_extension_t *extensions)
+ trn_extension_t *extensions)
{
ssize_t ret;
size_t dos_ext_encoded_len;
uint8_t *field_array;
- trn_cell_extension_field_t *field = NULL;
+ trn_extension_field_t *field = NULL;
trn_cell_extension_dos_t *dos_ext = NULL;
tor_assert(service_config);
tor_assert(extensions);
/* We are creating a cell extension field of the type DoS. */
- field = trn_cell_extension_field_new();
- trn_cell_extension_field_set_field_type(field,
+ field = trn_extension_field_new();
+ trn_extension_field_set_field_type(field,
TRUNNEL_CELL_EXTENSION_TYPE_DOS);
/* Build DoS extension field. We will put in two parameters. */
@@ -490,24 +593,23 @@ build_establish_intro_dos_extension(const hs_service_config_t *service_config,
}
dos_ext_encoded_len = ret;
/* Set length field and the field array size length. */
- trn_cell_extension_field_set_field_len(field, dos_ext_encoded_len);
- trn_cell_extension_field_setlen_field(field, dos_ext_encoded_len);
+ trn_extension_field_set_field_len(field, dos_ext_encoded_len);
+ trn_extension_field_setlen_field(field, dos_ext_encoded_len);
/* Encode the DoS extension into the cell extension field. */
- field_array = trn_cell_extension_field_getarray_field(field);
+ field_array = trn_extension_field_getarray_field(field);
ret = trn_cell_extension_dos_encode(field_array,
- trn_cell_extension_field_getlen_field(field), dos_ext);
+ trn_extension_field_getlen_field(field), dos_ext);
if (BUG(ret <= 0)) {
goto err;
}
tor_assert(ret == (ssize_t) dos_ext_encoded_len);
/* Finally, encode field into the cell extension. */
- trn_cell_extension_add_fields(extensions, field);
+ trn_extension_add_fields(extensions, field);
/* We've just add an extension field to the cell extensions so increment the
* total number. */
- trn_cell_extension_set_num(extensions,
- trn_cell_extension_get_num(extensions) + 1);
+ trn_extension_set_num(extensions, trn_extension_get_num(extensions) + 1);
/* Cleanup. DoS extension has been encoded at this point. */
trn_cell_extension_dos_free(dos_ext);
@@ -515,7 +617,7 @@ build_establish_intro_dos_extension(const hs_service_config_t *service_config,
return 0;
err:
- trn_cell_extension_field_free(field);
+ trn_extension_field_free(field);
trn_cell_extension_dos_free(dos_ext);
return -1;
}
@@ -526,18 +628,18 @@ build_establish_intro_dos_extension(const hs_service_config_t *service_config,
/** Allocate and build all the ESTABLISH_INTRO cell extension. The given
* extensions pointer is always set to a valid cell extension object. */
-STATIC trn_cell_extension_t *
+STATIC trn_extension_t *
build_establish_intro_extensions(const hs_service_config_t *service_config,
const hs_service_intro_point_t *ip)
{
int ret;
- trn_cell_extension_t *extensions;
+ trn_extension_t *extensions;
tor_assert(service_config);
tor_assert(ip);
- extensions = trn_cell_extension_new();
- trn_cell_extension_set_num(extensions, 0);
+ extensions = trn_extension_new();
+ trn_extension_set_num(extensions, 0);
/* If the defense has been enabled service side (by the operator with a
* torrc option) and the intro point does support it. */
@@ -568,7 +670,7 @@ hs_cell_build_establish_intro(const char *circ_nonce,
ssize_t cell_len = -1;
uint16_t sig_len = ED25519_SIG_LEN;
trn_cell_establish_intro_t *cell = NULL;
- trn_cell_extension_t *extensions;
+ trn_extension_t *extensions;
tor_assert(circ_nonce);
tor_assert(service_config);
@@ -692,6 +794,70 @@ hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
return ret;
}
+/** Parse the cell PoW solution extension. Return 0 on success and data
+ * structure is updated with the PoW effort. Return -1 on any kind of error
+ * including if PoW couldn't be verified. */
+static int
+handle_introduce2_encrypted_cell_pow_extension(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ const trn_extension_field_t *field,
+ hs_cell_introduce2_data_t *data)
+{
+ int ret = -1;
+ trn_cell_extension_pow_t *pow = NULL;
+ hs_pow_solution_t sol;
+
+ tor_assert(field);
+ tor_assert(ip);
+
+ if (!service->state.pow_state) {
+ log_info(LD_REND, "Unsolicited PoW solution in INTRODUCE2 request.");
+ goto end;
+ }
+
+ if (trn_cell_extension_pow_parse(&pow,
+ trn_extension_field_getconstarray_field(field),
+ trn_extension_field_getlen_field(field)) < 0) {
+ goto end;
+ }
+
+ /* There is only one version supported at the moment so validate we at least
+ * have that. */
+ if (trn_cell_extension_pow_get_pow_version(pow) !=
+ TRUNNEL_POW_VERSION_EQUIX) {
+ log_debug(LD_REND, "Unsupported PoW version. Malformed INTRODUCE2");
+ goto end;
+ }
+
+ /* Effort E */
+ sol.effort = trn_cell_extension_pow_get_pow_effort(pow);
+ /* Seed C */
+ memcpy(sol.seed_head, trn_cell_extension_pow_getconstarray_pow_seed(pow),
+ HS_POW_SEED_HEAD_LEN);
+ /* Nonce N */
+ memcpy(sol.nonce, trn_cell_extension_pow_getconstarray_pow_nonce(pow),
+ HS_POW_NONCE_LEN);
+ /* Solution S */
+ memcpy(sol.equix_solution,
+ trn_cell_extension_pow_getconstarray_pow_solution(pow),
+ HS_POW_EQX_SOL_LEN);
+
+ if (hs_pow_verify(&ip->blinded_id, service->state.pow_state, &sol)) {
+ log_info(LD_REND, "PoW INTRODUCE2 request failed to verify.");
+ goto end;
+ }
+
+ log_info(LD_REND, "PoW INTRODUCE2 request successfully verified.");
+ data->rdv_data.pow_effort = sol.effort;
+
+ /* Successfully parsed and verified the PoW solution */
+ ret = 0;
+
+ end:
+ trn_cell_extension_pow_free(pow);
+ return ret;
+}
+
/** For the encrypted INTRO2 cell in <b>encrypted_section</b>, use the crypto
* material in <b>data</b> to compute the right ntor keys. Also validate the
* INTRO2 MAC to ensure that the keys are the right ones.
@@ -711,7 +877,7 @@ get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data,
data->n_subcredentials,
data->subcredentials,
encrypted_section,
- &data->client_pk);
+ &data->rdv_data.client_pk);
if (intro_keys == NULL) {
log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to "
"compute key material");
@@ -760,6 +926,45 @@ get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data,
return intro_keys_result;
}
+/** Parse the given INTRODUCE cell extension. Update the data object
+ * accordingly depending on the extension. Return 0 if it validated
+ * correctly, or return -1 if it is malformed (for example because it
+ * includes a PoW that doesn't verify). */
+static int
+parse_introduce_cell_extension(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ hs_cell_introduce2_data_t *data,
+ const trn_extension_field_t *field)
+{
+ int ret = 0;
+ trn_extension_field_cc_t *cc_field = NULL;
+
+ tor_assert(data);
+ tor_assert(field);
+
+ switch (trn_extension_field_get_field_type(field)) {
+ case TRUNNEL_EXT_TYPE_CC_REQUEST:
+ /* CC requests, enable it. */
+ data->rdv_data.cc_enabled = 1;
+ data->pv.protocols_known = 1;
+ data->pv.supports_congestion_control = data->rdv_data.cc_enabled;
+ break;
+ case TRUNNEL_EXT_TYPE_POW:
+ /* PoW request. If successful, the effort is put in the data. */
+ if (handle_introduce2_encrypted_cell_pow_extension(service, ip,
+ field, data) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_REND, "Invalid PoW cell extension.");
+ ret = -1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ trn_extension_field_cc_free(cc_field);
+ return ret;
+}
+
/** Parse the INTRODUCE2 cell using data which contains everything we need to
* do so and contains the destination buffers of information we extract and
* compute from the cell. Return 0 on success else a negative value. The
@@ -767,7 +972,8 @@ get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data,
ssize_t
hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
const origin_circuit_t *circ,
- const hs_service_t *service)
+ const hs_service_t *service,
+ const hs_service_intro_point_t *ip)
{
int ret = -1;
time_t elapsed;
@@ -818,7 +1024,7 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
* guaranteed to exist because of the length check above). We are gonna use
* the client public key to compute the ntor keys and decrypt the payload:
*/
- memcpy(&data->client_pk.public_key, encrypted_section,
+ memcpy(&data->rdv_data.client_pk.public_key, encrypted_section,
CURVE25519_PUBKEY_LEN);
/* Get the right INTRODUCE2 ntor keys and verify the cell MAC */
@@ -834,12 +1040,13 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
{
/* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */
const uint8_t *encrypted_data =
- encrypted_section + sizeof(data->client_pk);
+ encrypted_section + sizeof(data->rdv_data.client_pk);
/* It's symmetric encryption so it's correct to use the ENCRYPTED length
* for decryption. Computes the length of ENCRYPTED_DATA meaning removing
* the CLIENT_PK and MAC length. */
size_t encrypted_data_len =
- encrypted_section_len - (sizeof(data->client_pk) + DIGEST256_LEN);
+ encrypted_section_len -
+ (sizeof(data->rdv_data.client_pk) + DIGEST256_LEN);
/* This decrypts the ENCRYPTED_DATA section of the cell. */
decrypted = decrypt_introduce2(intro_keys->enc_key,
@@ -866,12 +1073,12 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
/* Extract onion key and rendezvous cookie from the cell used for the
* rendezvous point circuit e2e encryption. */
- memcpy(data->onion_pk.public_key,
+ memcpy(data->rdv_data.onion_pk.public_key,
trn_cell_introduce_encrypted_getconstarray_onion_key(enc_cell),
CURVE25519_PUBKEY_LEN);
- memcpy(data->rendezvous_cookie,
+ memcpy(data->rdv_data.rendezvous_cookie,
trn_cell_introduce_encrypted_getconstarray_rend_cookie(enc_cell),
- sizeof(data->rendezvous_cookie));
+ sizeof(data->rdv_data.rendezvous_cookie));
/* Extract rendezvous link specifiers. */
for (size_t idx = 0;
@@ -885,12 +1092,36 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
if (BUG(!lspec_dup)) {
goto done;
}
- smartlist_add(data->link_specifiers, lspec_dup);
+ smartlist_add(data->rdv_data.link_specifiers, lspec_dup);
+ }
+
+ /* Extract any extensions. */
+ const trn_extension_t *extensions =
+ trn_cell_introduce_encrypted_get_extensions(enc_cell);
+ if (extensions != NULL) {
+ for (size_t idx = 0; idx < trn_extension_get_num(extensions); idx++) {
+ const trn_extension_field_t *field =
+ trn_extension_getconst_fields(extensions, idx);
+ if (BUG(field == NULL)) {
+ /* The number of extensions should match the number of fields. */
+ break;
+ }
+ if (parse_introduce_cell_extension(service, ip, data, field) < 0) {
+ goto done;
+ }
+ }
+ }
+
+ /* If the client asked for congestion control, but we don't support it,
+ * that's a failure. It should not have asked, based on our descriptor. */
+ if (data->rdv_data.cc_enabled && !congestion_control_enabled()) {
+ goto done;
}
/* Success. */
ret = 0;
- log_info(LD_REND, "Valid INTRODUCE2 cell. Launching rendezvous circuit.");
+ log_info(LD_REND,
+ "Valid INTRODUCE2 cell. Willing to launch rendezvous circuit.");
done:
if (intro_keys) {
@@ -947,7 +1178,7 @@ hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
{
ssize_t cell_len;
trn_cell_introduce1_t *cell;
- trn_cell_extension_t *ext;
+ trn_extension_t *ext;
tor_assert(data);
tor_assert(cell_out);
@@ -956,9 +1187,9 @@ hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
tor_assert(cell);
/* Set extension data. None are used. */
- ext = trn_cell_extension_new();
+ ext = trn_extension_new();
tor_assert(ext);
- trn_cell_extension_set_num(ext, 0);
+ trn_extension_set_num(ext, 0);
trn_cell_introduce1_set_extensions(cell, ext);
/* Set the authentication key. */