diff options
Diffstat (limited to 'src/feature/hs/hs_cell.c')
-rw-r--r-- | src/feature/hs/hs_cell.c | 301 |
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. */ |