Loading src/feature/hs/hs_cell.c +71 −7 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #include "feature/hs_common/replaycache.h" #include "feature/hs/hs_cell.h" #include "feature/hs/hs_ob.h" #include "core/crypto/hs_ntor.h" #include "core/or/origin_circuit_st.h" Loading Loading @@ -802,6 +803,47 @@ get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data, return intro_keys; } /** Return the newly allocated intro keys using the given service * configuration and INTRODUCE2 data for the cell encrypted section. * * Every onion balance configured master key will be tried. If NULL is * returned, no hit was found for the onion balance keys. */ static hs_ntor_intro_cell_keys_t * get_intro2_keys_as_ob(const hs_service_config_t *config, const hs_cell_introduce2_data_t *data, const uint8_t *encrypted_section, size_t encrypted_section_len) { uint8_t *ob_subcreds = NULL; size_t ob_num_subcreds; hs_ntor_intro_cell_keys_t *intro_keys = NULL; ob_num_subcreds = hs_ob_get_subcredentials(config, &ob_subcreds); if (!ob_num_subcreds) { /* We are _not_ an OB instance since no configured master onion key(s) * were found and thus no subcredentials were built. */ goto end; } for (size_t idx = 0; idx < ob_num_subcreds; idx++) { /* Copy current data into a new INTRO2 cell data. We will then change the * subcredential in order to validate. */ hs_cell_introduce2_data_t new_data = *data; new_data.subcredential = &(ob_subcreds[idx * DIGEST256_LEN]); intro_keys = get_introduce2_keys_and_verify_mac(&new_data, encrypted_section, encrypted_section_len); if (intro_keys) { /* It validates. We have a hit as an onion balance instance. */ goto end; } } end: tor_free(ob_subcreds); return intro_keys; } /** 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 Loading Loading @@ -856,6 +898,27 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data, goto done; } /* First bytes of the ENCRYPTED section are the client public key (they are * 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, CURVE25519_PUBKEY_LEN); /* If we are configured as an Onion Balance instance, we need to try * validation with the configured master public keys given in the config * file. This is because the master identity key and the blinded key is put * in the INTRODUCE2 cell by the client thus it will never validate with * this instance default public key. */ if (service->config.ob_master_pubkeys) { intro_keys = get_intro2_keys_as_ob(&service->config, data, encrypted_section, encrypted_section_len); } if (!intro_keys) { /* We are not an onion balance instance or no keys matched, fallback to * our default values. */ /* Get the right INTRODUCE2 ntor keys and verify the cell MAC */ intro_keys = get_introduce2_keys_and_verify_mac(data, encrypted_section, encrypted_section_len); Loading @@ -865,6 +928,7 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data, safe_str_client(service->onion_address)); goto done; } } { /* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */ Loading src/feature/hs/hs_ob.c +97 −0 Original line number Diff line number Diff line Loading @@ -158,6 +158,8 @@ ob_option_parse(hs_service_config_t *config, const ob_options_t *opts) goto end; } smartlist_add(config->ob_master_pubkeys, pubkey); log_info(LD_REND, "OnionBalance: MasterOnionAddress %s registered", line->value); } /* Success. */ ret = 1; Loading @@ -171,6 +173,26 @@ ob_option_parse(hs_service_config_t *config, const ob_options_t *opts) return ret; } /** For the given master public key and time period, compute the subcredential * and put them into subcredential. The subcredential parameter needs to be at * least DIGEST256_LEN in size. */ static void build_subcredential(const ed25519_public_key_t *pkey, uint64_t tp, uint8_t *subcredential) { ed25519_public_key_t blinded_pubkey; tor_assert(pkey); tor_assert(subcredential); hs_build_blinded_pubkey(pkey, NULL, 0, tp, &blinded_pubkey); hs_get_subcredential(pkey, &blinded_pubkey, subcredential); } /* * Public API. */ /** Read and parse the config file at fname on disk. The service config object * is populated with the options if any. * Loading Loading @@ -220,3 +242,78 @@ hs_ob_parse_config_file(hs_service_config_t *config) tor_free(config_file_path); return ret; } /** Compute all possible subcredentials for every onion master key in the * given service config object. The subcredentials is allocated and set as an * continous array containing all possible values. * * On success, return the number of subcredential put in the array which will * correspond to an arry of size: n * DIGEST256_LEN where DIGEST256_LEN is the * length of a single subcredential. * * If the given configuration object has no OB master keys configured, 0 is * returned and subcredentials is set to NULL. * * Otherwise, this can't fail. */ size_t hs_ob_get_subcredentials(const hs_service_config_t *config, uint8_t **subcredentials) { unsigned int num_pkeys, idx = 0; uint8_t *subcreds = NULL; const int steps[3] = {0, -1, 1}; const unsigned int num_steps = ARRAY_LENGTH(steps); const size_t subcred_len = DIGEST256_LEN; const uint64_t tp = hs_get_time_period_num(0); tor_assert(config); tor_assert(subcredentials); num_pkeys = smartlist_len(config->ob_master_pubkeys); if (!num_pkeys) { goto end; } /* Time to build all the subcredentials for each time period: the previous * one (-1), the current one (0) and the next one (1) for each configured * key in order to accomodate client and service consensus skew. * * If the client consensus after_time is at 23:00 but the service one is at * 01:00, the client will be using the previous time period where the * service will think it is the client next time period. Thus why we have * to try them all. * * The normal use case works because the service gets the descriptor object * that corresponds to the intro point's request, and because each * descriptor corresponds to a specific subcredential, we get the right * subcredential out of it, and use that to do the decryption. * * As a slight optimization, statistically, the current time period (0) will * be the one to work first so we'll put them first in the array to maximize * our chance of success. */ /* We use a flat array, not a smartlist_t, in order to minimize memory * allocation. This function is called for _each_ INTRODUCE2 cell arriving * on this instance and thus the less we allocate small chunks often, * usually the healthier our overall memory will be. * * Size of array is: length of a single subcredential multiplied by the * number of time period we need to compute and finally multiplied by the * total number of keys we are about to process. In other words, for each * key, we allocate 3 subcredential slots. */ subcreds = tor_malloc_zero(subcred_len * num_steps * num_pkeys); /* For each time period step. */ for (unsigned int i = 0; i < num_steps; i++) { SMARTLIST_FOREACH_BEGIN(config->ob_master_pubkeys, const ed25519_public_key_t *, pkey) { build_subcredential(pkey, tp + steps[i], &(subcreds[idx * subcred_len])); idx++; } SMARTLIST_FOREACH_END(pkey); } end: *subcredentials = subcreds; return idx; } src/feature/hs/hs_ob.h +3 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,9 @@ int hs_ob_parse_config_file(hs_service_config_t *config); size_t hs_ob_get_subcredentials(const hs_service_config_t *config, uint8_t **subcredentials); #ifdef HS_OB_PRIVATE typedef struct ob_options_t { Loading Loading
src/feature/hs/hs_cell.c +71 −7 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #include "feature/hs_common/replaycache.h" #include "feature/hs/hs_cell.h" #include "feature/hs/hs_ob.h" #include "core/crypto/hs_ntor.h" #include "core/or/origin_circuit_st.h" Loading Loading @@ -802,6 +803,47 @@ get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data, return intro_keys; } /** Return the newly allocated intro keys using the given service * configuration and INTRODUCE2 data for the cell encrypted section. * * Every onion balance configured master key will be tried. If NULL is * returned, no hit was found for the onion balance keys. */ static hs_ntor_intro_cell_keys_t * get_intro2_keys_as_ob(const hs_service_config_t *config, const hs_cell_introduce2_data_t *data, const uint8_t *encrypted_section, size_t encrypted_section_len) { uint8_t *ob_subcreds = NULL; size_t ob_num_subcreds; hs_ntor_intro_cell_keys_t *intro_keys = NULL; ob_num_subcreds = hs_ob_get_subcredentials(config, &ob_subcreds); if (!ob_num_subcreds) { /* We are _not_ an OB instance since no configured master onion key(s) * were found and thus no subcredentials were built. */ goto end; } for (size_t idx = 0; idx < ob_num_subcreds; idx++) { /* Copy current data into a new INTRO2 cell data. We will then change the * subcredential in order to validate. */ hs_cell_introduce2_data_t new_data = *data; new_data.subcredential = &(ob_subcreds[idx * DIGEST256_LEN]); intro_keys = get_introduce2_keys_and_verify_mac(&new_data, encrypted_section, encrypted_section_len); if (intro_keys) { /* It validates. We have a hit as an onion balance instance. */ goto end; } } end: tor_free(ob_subcreds); return intro_keys; } /** 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 Loading Loading @@ -856,6 +898,27 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data, goto done; } /* First bytes of the ENCRYPTED section are the client public key (they are * 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, CURVE25519_PUBKEY_LEN); /* If we are configured as an Onion Balance instance, we need to try * validation with the configured master public keys given in the config * file. This is because the master identity key and the blinded key is put * in the INTRODUCE2 cell by the client thus it will never validate with * this instance default public key. */ if (service->config.ob_master_pubkeys) { intro_keys = get_intro2_keys_as_ob(&service->config, data, encrypted_section, encrypted_section_len); } if (!intro_keys) { /* We are not an onion balance instance or no keys matched, fallback to * our default values. */ /* Get the right INTRODUCE2 ntor keys and verify the cell MAC */ intro_keys = get_introduce2_keys_and_verify_mac(data, encrypted_section, encrypted_section_len); Loading @@ -865,6 +928,7 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data, safe_str_client(service->onion_address)); goto done; } } { /* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */ Loading
src/feature/hs/hs_ob.c +97 −0 Original line number Diff line number Diff line Loading @@ -158,6 +158,8 @@ ob_option_parse(hs_service_config_t *config, const ob_options_t *opts) goto end; } smartlist_add(config->ob_master_pubkeys, pubkey); log_info(LD_REND, "OnionBalance: MasterOnionAddress %s registered", line->value); } /* Success. */ ret = 1; Loading @@ -171,6 +173,26 @@ ob_option_parse(hs_service_config_t *config, const ob_options_t *opts) return ret; } /** For the given master public key and time period, compute the subcredential * and put them into subcredential. The subcredential parameter needs to be at * least DIGEST256_LEN in size. */ static void build_subcredential(const ed25519_public_key_t *pkey, uint64_t tp, uint8_t *subcredential) { ed25519_public_key_t blinded_pubkey; tor_assert(pkey); tor_assert(subcredential); hs_build_blinded_pubkey(pkey, NULL, 0, tp, &blinded_pubkey); hs_get_subcredential(pkey, &blinded_pubkey, subcredential); } /* * Public API. */ /** Read and parse the config file at fname on disk. The service config object * is populated with the options if any. * Loading Loading @@ -220,3 +242,78 @@ hs_ob_parse_config_file(hs_service_config_t *config) tor_free(config_file_path); return ret; } /** Compute all possible subcredentials for every onion master key in the * given service config object. The subcredentials is allocated and set as an * continous array containing all possible values. * * On success, return the number of subcredential put in the array which will * correspond to an arry of size: n * DIGEST256_LEN where DIGEST256_LEN is the * length of a single subcredential. * * If the given configuration object has no OB master keys configured, 0 is * returned and subcredentials is set to NULL. * * Otherwise, this can't fail. */ size_t hs_ob_get_subcredentials(const hs_service_config_t *config, uint8_t **subcredentials) { unsigned int num_pkeys, idx = 0; uint8_t *subcreds = NULL; const int steps[3] = {0, -1, 1}; const unsigned int num_steps = ARRAY_LENGTH(steps); const size_t subcred_len = DIGEST256_LEN; const uint64_t tp = hs_get_time_period_num(0); tor_assert(config); tor_assert(subcredentials); num_pkeys = smartlist_len(config->ob_master_pubkeys); if (!num_pkeys) { goto end; } /* Time to build all the subcredentials for each time period: the previous * one (-1), the current one (0) and the next one (1) for each configured * key in order to accomodate client and service consensus skew. * * If the client consensus after_time is at 23:00 but the service one is at * 01:00, the client will be using the previous time period where the * service will think it is the client next time period. Thus why we have * to try them all. * * The normal use case works because the service gets the descriptor object * that corresponds to the intro point's request, and because each * descriptor corresponds to a specific subcredential, we get the right * subcredential out of it, and use that to do the decryption. * * As a slight optimization, statistically, the current time period (0) will * be the one to work first so we'll put them first in the array to maximize * our chance of success. */ /* We use a flat array, not a smartlist_t, in order to minimize memory * allocation. This function is called for _each_ INTRODUCE2 cell arriving * on this instance and thus the less we allocate small chunks often, * usually the healthier our overall memory will be. * * Size of array is: length of a single subcredential multiplied by the * number of time period we need to compute and finally multiplied by the * total number of keys we are about to process. In other words, for each * key, we allocate 3 subcredential slots. */ subcreds = tor_malloc_zero(subcred_len * num_steps * num_pkeys); /* For each time period step. */ for (unsigned int i = 0; i < num_steps; i++) { SMARTLIST_FOREACH_BEGIN(config->ob_master_pubkeys, const ed25519_public_key_t *, pkey) { build_subcredential(pkey, tp + steps[i], &(subcreds[idx * subcred_len])); idx++; } SMARTLIST_FOREACH_END(pkey); } end: *subcredentials = subcreds; return idx; }
src/feature/hs/hs_ob.h +3 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,9 @@ int hs_ob_parse_config_file(hs_service_config_t *config); size_t hs_ob_get_subcredentials(const hs_service_config_t *config, uint8_t **subcredentials); #ifdef HS_OB_PRIVATE typedef struct ob_options_t { Loading