mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 15:43:32 +01:00
Merge remote-tracking branch 'tor-github/pr/1685/head'
This commit is contained in:
commit
caa392a73a
4
changes/bug32709
Normal file
4
changes/bug32709
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
o Major features (v3 onion services):
|
||||||
|
- Allow v3 onion services to act as OnionBalance backend instances using
|
||||||
|
the HiddenServiceOnionBalanceInstance torrc option. Closes ticket 32709.
|
||||||
|
|
@ -3193,6 +3193,19 @@ The next section describes the per service options that can only be set
|
|||||||
The HAProxy version 1 protocol is described in detail at
|
The HAProxy version 1 protocol is described in detail at
|
||||||
https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
||||||
|
|
||||||
|
[[HiddenServiceOnionBalanceInstance]] **HiddenServiceOnionBalanceInstance** **0**|**1**::
|
||||||
|
|
||||||
|
If set to 1, this onion service becomes an OnionBalance instance and will
|
||||||
|
accept client connections destined to an OnionBalance frontend. In this
|
||||||
|
case, Tor expects to find a file named "ob_config" inside the
|
||||||
|
**HiddenServiceDir** directory with content:
|
||||||
|
+
|
||||||
|
MasterOnionAddress <frontend_onion_address>
|
||||||
|
+
|
||||||
|
where <frontend_onion_address> is the onion address of the OnionBalance
|
||||||
|
frontend (e.g. wrxdvcaqpuzakbfww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.onion).
|
||||||
|
|
||||||
|
|
||||||
[[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__::
|
[[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__::
|
||||||
The maximum number of simultaneous streams (connections) per rendezvous
|
The maximum number of simultaneous streams (connections) per rendezvous
|
||||||
circuit. The maximum value allowed is 65535. (Setting this to 0 will allow
|
circuit. The maximum value allowed is 65535. (Setting this to 0 will allow
|
||||||
|
@ -245,7 +245,7 @@ problem function-size /src/feature/hs/hs_cell.c:hs_cell_build_establish_intro()
|
|||||||
problem function-size /src/feature/hs/hs_cell.c:hs_cell_parse_introduce2() 152
|
problem function-size /src/feature/hs/hs_cell.c:hs_cell_parse_introduce2() 152
|
||||||
problem function-size /src/feature/hs/hs_client.c:send_introduce1() 103
|
problem function-size /src/feature/hs/hs_client.c:send_introduce1() 103
|
||||||
problem function-size /src/feature/hs/hs_common.c:hs_get_responsible_hsdirs() 102
|
problem function-size /src/feature/hs/hs_common.c:hs_get_responsible_hsdirs() 102
|
||||||
problem function-size /src/feature/hs/hs_config.c:config_service_v3() 107
|
problem function-size /src/feature/hs/hs_config.c:config_service_v3() 128
|
||||||
problem function-size /src/feature/hs/hs_config.c:config_generic_service() 138
|
problem function-size /src/feature/hs/hs_config.c:config_generic_service() 138
|
||||||
problem function-size /src/feature/hs/hs_descriptor.c:desc_encode_v3() 101
|
problem function-size /src/feature/hs/hs_descriptor.c:desc_encode_v3() 101
|
||||||
problem function-size /src/feature/hs/hs_descriptor.c:decrypt_desc_layer() 111
|
problem function-size /src/feature/hs/hs_descriptor.c:decrypt_desc_layer() 111
|
||||||
|
@ -510,6 +510,8 @@ static const config_var_t option_vars_[] = {
|
|||||||
LINELIST_S, RendConfigLines, NULL),
|
LINELIST_S, RendConfigLines, NULL),
|
||||||
VAR("HiddenServiceEnableIntroDoSBurstPerSec",
|
VAR("HiddenServiceEnableIntroDoSBurstPerSec",
|
||||||
LINELIST_S, RendConfigLines, NULL),
|
LINELIST_S, RendConfigLines, NULL),
|
||||||
|
VAR("HiddenServiceOnionBalanceInstance",
|
||||||
|
LINELIST_S, RendConfigLines, NULL),
|
||||||
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
|
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
|
||||||
V(HidServAuth, LINELIST, NULL),
|
V(HidServAuth, LINELIST, NULL),
|
||||||
V(ClientOnionAuthDir, FILENAME, NULL),
|
V(ClientOnionAuthDir, FILENAME, NULL),
|
||||||
|
@ -170,7 +170,7 @@ get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input,
|
|||||||
* necessary key material, and return 0. */
|
* necessary key material, and return 0. */
|
||||||
static void
|
static void
|
||||||
get_introduce1_key_material(const uint8_t *secret_input,
|
get_introduce1_key_material(const uint8_t *secret_input,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
|
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
|
||||||
{
|
{
|
||||||
uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
|
uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
|
||||||
@ -181,7 +181,7 @@ get_introduce1_key_material(const uint8_t *secret_input,
|
|||||||
/* Let's build info */
|
/* Let's build info */
|
||||||
ptr = info_blob;
|
ptr = info_blob;
|
||||||
APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
|
APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
|
||||||
APPEND(ptr, subcredential, DIGEST256_LEN);
|
APPEND(ptr, subcredential->subcred, SUBCRED_LEN);
|
||||||
tor_assert(ptr == info_blob + sizeof(info_blob));
|
tor_assert(ptr == info_blob + sizeof(info_blob));
|
||||||
|
|
||||||
/* Let's build the input to the KDF */
|
/* Let's build the input to the KDF */
|
||||||
@ -317,7 +317,7 @@ hs_ntor_client_get_introduce1_keys(
|
|||||||
const ed25519_public_key_t *intro_auth_pubkey,
|
const ed25519_public_key_t *intro_auth_pubkey,
|
||||||
const curve25519_public_key_t *intro_enc_pubkey,
|
const curve25519_public_key_t *intro_enc_pubkey,
|
||||||
const curve25519_keypair_t *client_ephemeral_enc_keypair,
|
const curve25519_keypair_t *client_ephemeral_enc_keypair,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
|
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
|
||||||
{
|
{
|
||||||
int bad = 0;
|
int bad = 0;
|
||||||
@ -450,7 +450,29 @@ hs_ntor_service_get_introduce1_keys(
|
|||||||
const ed25519_public_key_t *intro_auth_pubkey,
|
const ed25519_public_key_t *intro_auth_pubkey,
|
||||||
const curve25519_keypair_t *intro_enc_keypair,
|
const curve25519_keypair_t *intro_enc_keypair,
|
||||||
const curve25519_public_key_t *client_ephemeral_enc_pubkey,
|
const curve25519_public_key_t *client_ephemeral_enc_pubkey,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
|
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
|
||||||
|
{
|
||||||
|
return hs_ntor_service_get_introduce1_keys_multi(
|
||||||
|
intro_auth_pubkey,
|
||||||
|
intro_enc_keypair,
|
||||||
|
client_ephemeral_enc_pubkey,
|
||||||
|
1,
|
||||||
|
subcredential,
|
||||||
|
hs_ntor_intro_cell_keys_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As hs_ntor_service_get_introduce1_keys(), but take multiple subcredentials
|
||||||
|
* as input, and yield multiple sets of keys as output.
|
||||||
|
**/
|
||||||
|
int
|
||||||
|
hs_ntor_service_get_introduce1_keys_multi(
|
||||||
|
const struct ed25519_public_key_t *intro_auth_pubkey,
|
||||||
|
const struct curve25519_keypair_t *intro_enc_keypair,
|
||||||
|
const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
|
||||||
|
size_t n_subcredentials,
|
||||||
|
const hs_subcredential_t *subcredentials,
|
||||||
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
|
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
|
||||||
{
|
{
|
||||||
int bad = 0;
|
int bad = 0;
|
||||||
@ -460,7 +482,8 @@ hs_ntor_service_get_introduce1_keys(
|
|||||||
tor_assert(intro_auth_pubkey);
|
tor_assert(intro_auth_pubkey);
|
||||||
tor_assert(intro_enc_keypair);
|
tor_assert(intro_enc_keypair);
|
||||||
tor_assert(client_ephemeral_enc_pubkey);
|
tor_assert(client_ephemeral_enc_pubkey);
|
||||||
tor_assert(subcredential);
|
tor_assert(n_subcredentials >= 1);
|
||||||
|
tor_assert(subcredentials);
|
||||||
tor_assert(hs_ntor_intro_cell_keys_out);
|
tor_assert(hs_ntor_intro_cell_keys_out);
|
||||||
|
|
||||||
/* Compute EXP(X, b) */
|
/* Compute EXP(X, b) */
|
||||||
@ -476,13 +499,16 @@ hs_ntor_service_get_introduce1_keys(
|
|||||||
secret_input);
|
secret_input);
|
||||||
bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
|
bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < n_subcredentials; ++i) {
|
||||||
/* Get ENC_KEY and MAC_KEY! */
|
/* Get ENC_KEY and MAC_KEY! */
|
||||||
get_introduce1_key_material(secret_input, subcredential,
|
get_introduce1_key_material(secret_input, &subcredentials[i],
|
||||||
hs_ntor_intro_cell_keys_out);
|
&hs_ntor_intro_cell_keys_out[i]);
|
||||||
|
}
|
||||||
|
|
||||||
memwipe(secret_input, 0, sizeof(secret_input));
|
memwipe(secret_input, 0, sizeof(secret_input));
|
||||||
if (bad) {
|
if (bad) {
|
||||||
memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
|
memwipe(hs_ntor_intro_cell_keys_out, 0,
|
||||||
|
sizeof(hs_ntor_intro_cell_keys_t) * n_subcredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bad ? -1 : 0;
|
return bad ? -1 : 0;
|
||||||
|
@ -35,11 +35,20 @@ typedef struct hs_ntor_rend_cell_keys_t {
|
|||||||
uint8_t ntor_key_seed[DIGEST256_LEN];
|
uint8_t ntor_key_seed[DIGEST256_LEN];
|
||||||
} hs_ntor_rend_cell_keys_t;
|
} hs_ntor_rend_cell_keys_t;
|
||||||
|
|
||||||
|
#define SUBCRED_LEN DIGEST256_LEN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 'subcredential' used to prove knowledge of a hidden service.
|
||||||
|
**/
|
||||||
|
typedef struct hs_subcredential_t {
|
||||||
|
uint8_t subcred[SUBCRED_LEN];
|
||||||
|
} hs_subcredential_t;
|
||||||
|
|
||||||
int hs_ntor_client_get_introduce1_keys(
|
int hs_ntor_client_get_introduce1_keys(
|
||||||
const struct ed25519_public_key_t *intro_auth_pubkey,
|
const struct ed25519_public_key_t *intro_auth_pubkey,
|
||||||
const struct curve25519_public_key_t *intro_enc_pubkey,
|
const struct curve25519_public_key_t *intro_enc_pubkey,
|
||||||
const struct curve25519_keypair_t *client_ephemeral_enc_keypair,
|
const struct curve25519_keypair_t *client_ephemeral_enc_keypair,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
|
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
|
||||||
|
|
||||||
int hs_ntor_client_get_rendezvous1_keys(
|
int hs_ntor_client_get_rendezvous1_keys(
|
||||||
@ -49,11 +58,19 @@ int hs_ntor_client_get_rendezvous1_keys(
|
|||||||
const struct curve25519_public_key_t *service_ephemeral_rend_pubkey,
|
const struct curve25519_public_key_t *service_ephemeral_rend_pubkey,
|
||||||
hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out);
|
hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out);
|
||||||
|
|
||||||
|
int hs_ntor_service_get_introduce1_keys_multi(
|
||||||
|
const struct ed25519_public_key_t *intro_auth_pubkey,
|
||||||
|
const struct curve25519_keypair_t *intro_enc_keypair,
|
||||||
|
const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
|
||||||
|
size_t n_subcredentials,
|
||||||
|
const hs_subcredential_t *subcredentials,
|
||||||
|
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
|
||||||
|
|
||||||
int hs_ntor_service_get_introduce1_keys(
|
int hs_ntor_service_get_introduce1_keys(
|
||||||
const struct ed25519_public_key_t *intro_auth_pubkey,
|
const struct ed25519_public_key_t *intro_auth_pubkey,
|
||||||
const struct curve25519_keypair_t *intro_enc_keypair,
|
const struct curve25519_keypair_t *intro_enc_keypair,
|
||||||
const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
|
const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
|
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
|
||||||
|
|
||||||
int hs_ntor_service_get_rendezvous1_keys(
|
int hs_ntor_service_get_rendezvous1_keys(
|
||||||
|
@ -2819,8 +2819,8 @@ extend_info_dup(extend_info_t *info)
|
|||||||
* If there is no chosen exit, or if we don't know the node_t for
|
* If there is no chosen exit, or if we don't know the node_t for
|
||||||
* the chosen exit, return NULL.
|
* the chosen exit, return NULL.
|
||||||
*/
|
*/
|
||||||
const node_t *
|
MOCK_IMPL(const node_t *,
|
||||||
build_state_get_exit_node(cpath_build_state_t *state)
|
build_state_get_exit_node,(cpath_build_state_t *state))
|
||||||
{
|
{
|
||||||
if (!state || !state->chosen_exit)
|
if (!state || !state->chosen_exit)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -66,7 +66,8 @@ int circuit_can_use_tap(const origin_circuit_t *circ);
|
|||||||
int circuit_has_usable_onion_key(const origin_circuit_t *circ);
|
int circuit_has_usable_onion_key(const origin_circuit_t *circ);
|
||||||
int extend_info_has_preferred_onion_key(const extend_info_t* ei);
|
int extend_info_has_preferred_onion_key(const extend_info_t* ei);
|
||||||
const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state);
|
const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state);
|
||||||
const node_t *build_state_get_exit_node(cpath_build_state_t *state);
|
MOCK_DECL(const node_t *,
|
||||||
|
build_state_get_exit_node,(cpath_build_state_t *state));
|
||||||
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
|
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
|
||||||
|
|
||||||
struct circuit_guard_state_t;
|
struct circuit_guard_state_t;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "feature/hs_common/replaycache.h"
|
#include "feature/hs_common/replaycache.h"
|
||||||
|
|
||||||
#include "feature/hs/hs_cell.h"
|
#include "feature/hs/hs_cell.h"
|
||||||
|
#include "feature/hs/hs_ob.h"
|
||||||
#include "core/crypto/hs_ntor.h"
|
#include "core/crypto/hs_ntor.h"
|
||||||
|
|
||||||
#include "core/or/origin_circuit_st.h"
|
#include "core/or/origin_circuit_st.h"
|
||||||
@ -67,14 +68,17 @@ compute_introduce_mac(const uint8_t *encoded_cell, size_t encoded_cell_len,
|
|||||||
memwipe(mac_msg, 0, sizeof(mac_msg));
|
memwipe(mac_msg, 0, sizeof(mac_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** From a set of keys, subcredential and the ENCRYPTED section of an
|
/**
|
||||||
* INTRODUCE2 cell, return a newly allocated intro cell keys structure.
|
* From a set of keys, a list of subcredentials, and the ENCRYPTED section of
|
||||||
* Finally, the client public key is copied in client_pk. On error, return
|
* an INTRODUCE2 cell, return an array of newly allocated intro cell keys
|
||||||
* NULL. */
|
* structures. Finally, the client public key is copied in client_pk. On
|
||||||
|
* error, return NULL.
|
||||||
|
**/
|
||||||
static hs_ntor_intro_cell_keys_t *
|
static hs_ntor_intro_cell_keys_t *
|
||||||
get_introduce2_key_material(const ed25519_public_key_t *auth_key,
|
get_introduce2_key_material(const ed25519_public_key_t *auth_key,
|
||||||
const curve25519_keypair_t *enc_key,
|
const curve25519_keypair_t *enc_key,
|
||||||
const uint8_t *subcredential,
|
size_t n_subcredentials,
|
||||||
|
const hs_subcredential_t *subcredentials,
|
||||||
const uint8_t *encrypted_section,
|
const uint8_t *encrypted_section,
|
||||||
curve25519_public_key_t *client_pk)
|
curve25519_public_key_t *client_pk)
|
||||||
{
|
{
|
||||||
@ -82,17 +86,19 @@ get_introduce2_key_material(const ed25519_public_key_t *auth_key,
|
|||||||
|
|
||||||
tor_assert(auth_key);
|
tor_assert(auth_key);
|
||||||
tor_assert(enc_key);
|
tor_assert(enc_key);
|
||||||
tor_assert(subcredential);
|
tor_assert(n_subcredentials > 0);
|
||||||
|
tor_assert(subcredentials);
|
||||||
tor_assert(encrypted_section);
|
tor_assert(encrypted_section);
|
||||||
tor_assert(client_pk);
|
tor_assert(client_pk);
|
||||||
|
|
||||||
keys = tor_malloc_zero(sizeof(*keys));
|
keys = tor_calloc(n_subcredentials, sizeof(hs_ntor_intro_cell_keys_t));
|
||||||
|
|
||||||
/* First bytes of the ENCRYPTED section are the client public key. */
|
/* First bytes of the ENCRYPTED section are the client public key. */
|
||||||
memcpy(client_pk->public_key, encrypted_section, CURVE25519_PUBKEY_LEN);
|
memcpy(client_pk->public_key, encrypted_section, CURVE25519_PUBKEY_LEN);
|
||||||
|
|
||||||
if (hs_ntor_service_get_introduce1_keys(auth_key, enc_key, client_pk,
|
if (hs_ntor_service_get_introduce1_keys_multi(auth_key, enc_key, client_pk,
|
||||||
subcredential, keys) < 0) {
|
n_subcredentials,
|
||||||
|
subcredentials, keys) < 0) {
|
||||||
/* Don't rely on the caller to wipe this on error. */
|
/* Don't rely on the caller to wipe this on error. */
|
||||||
memwipe(client_pk, 0, sizeof(curve25519_public_key_t));
|
memwipe(client_pk, 0, sizeof(curve25519_public_key_t));
|
||||||
tor_free(keys);
|
tor_free(keys);
|
||||||
@ -747,6 +753,74 @@ hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
|
|||||||
return ret;
|
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.
|
||||||
|
*
|
||||||
|
* Return NULL on failure to either produce the key material or on MAC
|
||||||
|
* valication. Else a newly allocated intro keys object. */
|
||||||
|
static hs_ntor_intro_cell_keys_t *
|
||||||
|
get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data,
|
||||||
|
const uint8_t *encrypted_section,
|
||||||
|
size_t encrypted_section_len)
|
||||||
|
{
|
||||||
|
hs_ntor_intro_cell_keys_t *intro_keys = NULL;
|
||||||
|
hs_ntor_intro_cell_keys_t *intro_keys_result = NULL;
|
||||||
|
|
||||||
|
/* Build the key material out of the key material found in the cell. */
|
||||||
|
intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp,
|
||||||
|
data->n_subcredentials,
|
||||||
|
data->subcredentials,
|
||||||
|
encrypted_section,
|
||||||
|
&data->client_pk);
|
||||||
|
if (intro_keys == NULL) {
|
||||||
|
log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to "
|
||||||
|
"compute key material");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we are not about to underflow. */
|
||||||
|
if (BUG(encrypted_section_len < DIGEST256_LEN)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate MAC from the cell and our computed key material. The MAC field
|
||||||
|
* in the cell is at the end of the encrypted section. */
|
||||||
|
intro_keys_result = tor_malloc_zero(sizeof(*intro_keys_result));
|
||||||
|
for (unsigned i = 0; i < data->n_subcredentials; ++i) {
|
||||||
|
uint8_t mac[DIGEST256_LEN];
|
||||||
|
|
||||||
|
/* The MAC field is at the very end of the ENCRYPTED section. */
|
||||||
|
size_t mac_offset = encrypted_section_len - sizeof(mac);
|
||||||
|
/* Compute the MAC. Use the entire encoded payload with a length up to the
|
||||||
|
* ENCRYPTED section. */
|
||||||
|
compute_introduce_mac(data->payload,
|
||||||
|
data->payload_len - encrypted_section_len,
|
||||||
|
encrypted_section, encrypted_section_len,
|
||||||
|
intro_keys[i].mac_key,
|
||||||
|
sizeof(intro_keys[i].mac_key),
|
||||||
|
mac, sizeof(mac));
|
||||||
|
/* Time-invariant conditional copy: if the MAC is what we expected, then
|
||||||
|
* set intro_keys_result to intro_keys[i]. Otherwise, don't: but don't
|
||||||
|
* leak which one it was! */
|
||||||
|
bool equal = tor_memeq(mac, encrypted_section + mac_offset, sizeof(mac));
|
||||||
|
memcpy_if_true_timei(equal, intro_keys_result, &intro_keys[i],
|
||||||
|
sizeof(*intro_keys_result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We no longer need intro_keys. */
|
||||||
|
memwipe(intro_keys, 0,
|
||||||
|
sizeof(hs_ntor_intro_cell_keys_t) * data->n_subcredentials);
|
||||||
|
tor_free(intro_keys);
|
||||||
|
|
||||||
|
if (safe_mem_is_zero(intro_keys_result, sizeof(*intro_keys_result))) {
|
||||||
|
log_info(LD_REND, "Invalid MAC validation for INTRODUCE2 cell");
|
||||||
|
tor_free(intro_keys_result); /* sets intro_keys_result to NULL */
|
||||||
|
}
|
||||||
|
|
||||||
|
return intro_keys_result;
|
||||||
|
}
|
||||||
|
|
||||||
/** Parse the INTRODUCE2 cell using data which contains everything we need to
|
/** Parse the INTRODUCE2 cell using data which contains everything we need to
|
||||||
* do so and contains the destination buffers of information we extract and
|
* 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
|
* compute from the cell. Return 0 on success else a negative value. The
|
||||||
@ -795,46 +869,28 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
|
|||||||
/* Check our replay cache for this introduction point. */
|
/* Check our replay cache for this introduction point. */
|
||||||
if (replaycache_add_test_and_elapsed(data->replay_cache, encrypted_section,
|
if (replaycache_add_test_and_elapsed(data->replay_cache, encrypted_section,
|
||||||
encrypted_section_len, &elapsed)) {
|
encrypted_section_len, &elapsed)) {
|
||||||
log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the"
|
log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the "
|
||||||
"same ENCRYPTED section was seen %ld seconds ago. "
|
"same ENCRYPTED section was seen %ld seconds ago. "
|
||||||
"Dropping cell.", (long int) elapsed);
|
"Dropping cell.", (long int) elapsed);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build the key material out of the key material found in the cell. */
|
/* First bytes of the ENCRYPTED section are the client public key (they are
|
||||||
intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp,
|
* guaranteed to exist because of the length check above). We are gonna use
|
||||||
data->subcredential,
|
* the client public key to compute the ntor keys and decrypt the payload:
|
||||||
encrypted_section,
|
*/
|
||||||
&data->client_pk);
|
memcpy(&data->client_pk.public_key, encrypted_section,
|
||||||
if (intro_keys == NULL) {
|
CURVE25519_PUBKEY_LEN);
|
||||||
log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to "
|
|
||||||
"compute key material on circuit %u for service %s",
|
|
||||||
TO_CIRCUIT(circ)->n_circ_id,
|
|
||||||
safe_str_client(service->onion_address));
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate MAC from the cell and our computed key material. The MAC field
|
/* Get the right INTRODUCE2 ntor keys and verify the cell MAC */
|
||||||
* in the cell is at the end of the encrypted section. */
|
intro_keys = get_introduce2_keys_and_verify_mac(data, encrypted_section,
|
||||||
{
|
encrypted_section_len);
|
||||||
uint8_t mac[DIGEST256_LEN];
|
if (!intro_keys) {
|
||||||
/* The MAC field is at the very end of the ENCRYPTED section. */
|
log_warn(LD_REND, "Could not get valid INTRO2 keys on circuit %u "
|
||||||
size_t mac_offset = encrypted_section_len - sizeof(mac);
|
"for service %s", TO_CIRCUIT(circ)->n_circ_id,
|
||||||
/* Compute the MAC. Use the entire encoded payload with a length up to the
|
|
||||||
* ENCRYPTED section. */
|
|
||||||
compute_introduce_mac(data->payload,
|
|
||||||
data->payload_len - encrypted_section_len,
|
|
||||||
encrypted_section, encrypted_section_len,
|
|
||||||
intro_keys->mac_key, sizeof(intro_keys->mac_key),
|
|
||||||
mac, sizeof(mac));
|
|
||||||
if (tor_memcmp(mac, encrypted_section + mac_offset, sizeof(mac))) {
|
|
||||||
log_info(LD_REND, "Invalid MAC validation for INTRODUCE2 cell on "
|
|
||||||
"circuit %u for service %s",
|
|
||||||
TO_CIRCUIT(circ)->n_circ_id,
|
|
||||||
safe_str_client(service->onion_address));
|
safe_str_client(service->onion_address));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
/* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */
|
/* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
* 3.2.2 of the specification). Below this value, the cell must be padded. */
|
* 3.2.2 of the specification). Below this value, the cell must be padded. */
|
||||||
#define HS_CELL_INTRODUCE1_MIN_SIZE 246
|
#define HS_CELL_INTRODUCE1_MIN_SIZE 246
|
||||||
|
|
||||||
|
struct hs_subcredential_t;
|
||||||
|
|
||||||
/** This data structure contains data that we need to build an INTRODUCE1 cell
|
/** This data structure contains data that we need to build an INTRODUCE1 cell
|
||||||
* used by the INTRODUCE1 build function. */
|
* used by the INTRODUCE1 build function. */
|
||||||
typedef struct hs_cell_introduce1_data_t {
|
typedef struct hs_cell_introduce1_data_t {
|
||||||
@ -29,7 +31,7 @@ typedef struct hs_cell_introduce1_data_t {
|
|||||||
/** Introduction point encryption public key. */
|
/** Introduction point encryption public key. */
|
||||||
const curve25519_public_key_t *enc_pk;
|
const curve25519_public_key_t *enc_pk;
|
||||||
/** Subcredentials of the service. */
|
/** Subcredentials of the service. */
|
||||||
const uint8_t *subcredential;
|
const struct hs_subcredential_t *subcredential;
|
||||||
/** Onion public key for the ntor handshake. */
|
/** Onion public key for the ntor handshake. */
|
||||||
const curve25519_public_key_t *onion_pk;
|
const curve25519_public_key_t *onion_pk;
|
||||||
/** Rendezvous cookie. */
|
/** Rendezvous cookie. */
|
||||||
@ -55,9 +57,14 @@ typedef struct hs_cell_introduce2_data_t {
|
|||||||
owned by the introduction point object through which we received the
|
owned by the introduction point object through which we received the
|
||||||
INTRO2 cell*/
|
INTRO2 cell*/
|
||||||
const curve25519_keypair_t *enc_kp;
|
const curve25519_keypair_t *enc_kp;
|
||||||
/** Subcredentials of the service. Pointer owned by the descriptor that owns
|
/**
|
||||||
the introduction point through which we received the INTRO2 cell. */
|
* Length of the subcredentials array below.
|
||||||
const uint8_t *subcredential;
|
**/
|
||||||
|
size_t n_subcredentials;
|
||||||
|
/** Array of <b>n_subcredentials</b> subcredentials for the service. Pointer
|
||||||
|
* owned by the descriptor that owns the introduction point through which we
|
||||||
|
* received the INTRO2 cell. */
|
||||||
|
const struct hs_subcredential_t *subcredentials;
|
||||||
/** Payload of the received encoded cell. */
|
/** Payload of the received encoded cell. */
|
||||||
const uint8_t *payload;
|
const uint8_t *payload;
|
||||||
/** Size of the payload of the received encoded cell. */
|
/** Size of the payload of the received encoded cell. */
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "feature/client/circpathbias.h"
|
#include "feature/client/circpathbias.h"
|
||||||
#include "feature/hs/hs_cell.h"
|
#include "feature/hs/hs_cell.h"
|
||||||
#include "feature/hs/hs_circuit.h"
|
#include "feature/hs/hs_circuit.h"
|
||||||
|
#include "feature/hs/hs_ob.h"
|
||||||
#include "feature/hs/hs_circuitmap.h"
|
#include "feature/hs/hs_circuitmap.h"
|
||||||
#include "feature/hs/hs_client.h"
|
#include "feature/hs/hs_client.h"
|
||||||
#include "feature/hs/hs_ident.h"
|
#include "feature/hs/hs_ident.h"
|
||||||
@ -367,10 +368,10 @@ get_service_anonymity_string(const hs_service_t *service)
|
|||||||
* success, a circuit identifier is attached to the circuit with the needed
|
* success, a circuit identifier is attached to the circuit with the needed
|
||||||
* data. This function will try to open a circuit for a maximum value of
|
* data. This function will try to open a circuit for a maximum value of
|
||||||
* MAX_REND_FAILURES then it will give up. */
|
* MAX_REND_FAILURES then it will give up. */
|
||||||
static void
|
MOCK_IMPL(STATIC void,
|
||||||
launch_rendezvous_point_circuit(const hs_service_t *service,
|
launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||||
const hs_service_intro_point_t *ip,
|
const hs_service_intro_point_t *ip,
|
||||||
const hs_cell_introduce2_data_t *data)
|
const hs_cell_introduce2_data_t *data))
|
||||||
{
|
{
|
||||||
int circ_needs_uptime;
|
int circ_needs_uptime;
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
@ -578,7 +579,7 @@ retry_service_rendezvous_point(const origin_circuit_t *circ)
|
|||||||
static int
|
static int
|
||||||
setup_introduce1_data(const hs_desc_intro_point_t *ip,
|
setup_introduce1_data(const hs_desc_intro_point_t *ip,
|
||||||
const node_t *rp_node,
|
const node_t *rp_node,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
hs_cell_introduce1_data_t *intro1_data)
|
hs_cell_introduce1_data_t *intro1_data)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
@ -958,6 +959,42 @@ hs_circ_handle_intro_established(const hs_service_t *service,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go into <b>data</b> and add the right subcredential to be able to handle
|
||||||
|
* this incoming cell.
|
||||||
|
*
|
||||||
|
* <b>desc_subcred</b> is the subcredential of the descriptor that corresponds
|
||||||
|
* to the intro point that received this intro request. This subcredential
|
||||||
|
* should be used if we are not an onionbalance instance.
|
||||||
|
*
|
||||||
|
* Return 0 if everything went well, or -1 in case of internal error.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_subcredential_for_handling_intro2_cell(const hs_service_t *service,
|
||||||
|
hs_cell_introduce2_data_t *data,
|
||||||
|
const hs_subcredential_t *desc_subcred)
|
||||||
|
{
|
||||||
|
/* Handle the simple case first: We are not an onionbalance instance and we
|
||||||
|
* should just use the regular descriptor subcredential */
|
||||||
|
if (!hs_ob_service_is_instance(service)) {
|
||||||
|
data->n_subcredentials = 1;
|
||||||
|
data->subcredentials = desc_subcred;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This should not happen since we should have made onionbalance
|
||||||
|
* subcredentials when we created our descriptors. */
|
||||||
|
if (BUG(!service->ob_subcreds)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are an onionbalance instance: */
|
||||||
|
data->n_subcredentials = service->n_ob_subcreds;
|
||||||
|
data->subcredentials = service->ob_subcreds;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** We just received an INTRODUCE2 cell on the established introduction circuit
|
/** We just received an INTRODUCE2 cell on the established introduction circuit
|
||||||
* circ. Handle the INTRODUCE2 payload of size payload_len for the given
|
* circ. Handle the INTRODUCE2 payload of size payload_len for the given
|
||||||
* circuit and service. This cell is associated with the intro point object ip
|
* circuit and service. This cell is associated with the intro point object ip
|
||||||
@ -966,7 +1003,7 @@ int
|
|||||||
hs_circ_handle_introduce2(const hs_service_t *service,
|
hs_circ_handle_introduce2(const hs_service_t *service,
|
||||||
const origin_circuit_t *circ,
|
const origin_circuit_t *circ,
|
||||||
hs_service_intro_point_t *ip,
|
hs_service_intro_point_t *ip,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
const uint8_t *payload, size_t payload_len)
|
const uint8_t *payload, size_t payload_len)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
@ -983,12 +1020,16 @@ hs_circ_handle_introduce2(const hs_service_t *service,
|
|||||||
* parsed, decrypted and key material computed correctly. */
|
* parsed, decrypted and key material computed correctly. */
|
||||||
data.auth_pk = &ip->auth_key_kp.pubkey;
|
data.auth_pk = &ip->auth_key_kp.pubkey;
|
||||||
data.enc_kp = &ip->enc_key_kp;
|
data.enc_kp = &ip->enc_key_kp;
|
||||||
data.subcredential = subcredential;
|
|
||||||
data.payload = payload;
|
data.payload = payload;
|
||||||
data.payload_len = payload_len;
|
data.payload_len = payload_len;
|
||||||
data.link_specifiers = smartlist_new();
|
data.link_specifiers = smartlist_new();
|
||||||
data.replay_cache = ip->replay_cache;
|
data.replay_cache = ip->replay_cache;
|
||||||
|
|
||||||
|
if (get_subcredential_for_handling_intro2_cell(service,
|
||||||
|
&data, subcredential)) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (hs_cell_parse_introduce2(&data, circ, service) < 0) {
|
if (hs_cell_parse_introduce2(&data, circ, service) < 0) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@ -1092,7 +1133,7 @@ int
|
|||||||
hs_circ_send_introduce1(origin_circuit_t *intro_circ,
|
hs_circ_send_introduce1(origin_circuit_t *intro_circ,
|
||||||
origin_circuit_t *rend_circ,
|
origin_circuit_t *rend_circ,
|
||||||
const hs_desc_intro_point_t *ip,
|
const hs_desc_intro_point_t *ip,
|
||||||
const uint8_t *subcredential)
|
const hs_subcredential_t *subcredential)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
ssize_t payload_len;
|
ssize_t payload_len;
|
||||||
|
@ -46,15 +46,16 @@ int hs_circ_handle_intro_established(const hs_service_t *service,
|
|||||||
origin_circuit_t *circ,
|
origin_circuit_t *circ,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payload_len);
|
size_t payload_len);
|
||||||
|
struct hs_subcredential_t;
|
||||||
int hs_circ_handle_introduce2(const hs_service_t *service,
|
int hs_circ_handle_introduce2(const hs_service_t *service,
|
||||||
const origin_circuit_t *circ,
|
const origin_circuit_t *circ,
|
||||||
hs_service_intro_point_t *ip,
|
hs_service_intro_point_t *ip,
|
||||||
const uint8_t *subcredential,
|
const struct hs_subcredential_t *subcredential,
|
||||||
const uint8_t *payload, size_t payload_len);
|
const uint8_t *payload, size_t payload_len);
|
||||||
int hs_circ_send_introduce1(origin_circuit_t *intro_circ,
|
int hs_circ_send_introduce1(origin_circuit_t *intro_circ,
|
||||||
origin_circuit_t *rend_circ,
|
origin_circuit_t *rend_circ,
|
||||||
const hs_desc_intro_point_t *ip,
|
const hs_desc_intro_point_t *ip,
|
||||||
const uint8_t *subcredential);
|
const struct hs_subcredential_t *subcredential);
|
||||||
int hs_circ_send_establish_rendezvous(origin_circuit_t *circ);
|
int hs_circ_send_establish_rendezvous(origin_circuit_t *circ);
|
||||||
|
|
||||||
/* e2e circuit API. */
|
/* e2e circuit API. */
|
||||||
@ -78,6 +79,12 @@ create_rp_circuit_identifier(const hs_service_t *service,
|
|||||||
const curve25519_public_key_t *server_pk,
|
const curve25519_public_key_t *server_pk,
|
||||||
const struct hs_ntor_rend_cell_keys_t *keys);
|
const struct hs_ntor_rend_cell_keys_t *keys);
|
||||||
|
|
||||||
|
struct hs_cell_introduce2_data_t;
|
||||||
|
MOCK_DECL(STATIC void,
|
||||||
|
launch_rendezvous_point_circuit,(const hs_service_t *service,
|
||||||
|
const hs_service_intro_point_t *ip,
|
||||||
|
const struct hs_cell_introduce2_data_t *data));
|
||||||
|
|
||||||
#endif /* defined(HS_CIRCUIT_PRIVATE) */
|
#endif /* defined(HS_CIRCUIT_PRIVATE) */
|
||||||
|
|
||||||
#endif /* !defined(TOR_HS_CIRCUIT_H) */
|
#endif /* !defined(TOR_HS_CIRCUIT_H) */
|
||||||
|
@ -646,7 +646,7 @@ send_introduce1(origin_circuit_t *intro_circ,
|
|||||||
|
|
||||||
/* Send the INTRODUCE1 cell. */
|
/* Send the INTRODUCE1 cell. */
|
||||||
if (hs_circ_send_introduce1(intro_circ, rend_circ, ip,
|
if (hs_circ_send_introduce1(intro_circ, rend_circ, ip,
|
||||||
desc->subcredential) < 0) {
|
&desc->subcredential) < 0) {
|
||||||
if (TO_CIRCUIT(intro_circ)->marked_for_close) {
|
if (TO_CIRCUIT(intro_circ)->marked_for_close) {
|
||||||
/* If the introduction circuit was closed, we were unable to send the
|
/* If the introduction circuit was closed, we were unable to send the
|
||||||
* cell for some reasons. In any case, the intro circuit has to be
|
* cell for some reasons. In any case, the intro circuit has to be
|
||||||
@ -1845,7 +1845,7 @@ hs_client_decode_descriptor(const char *desc_str,
|
|||||||
hs_descriptor_t **desc)
|
hs_descriptor_t **desc)
|
||||||
{
|
{
|
||||||
hs_desc_decode_status_t ret;
|
hs_desc_decode_status_t ret;
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
ed25519_public_key_t blinded_pubkey;
|
ed25519_public_key_t blinded_pubkey;
|
||||||
hs_client_service_authorization_t *client_auth = NULL;
|
hs_client_service_authorization_t *client_auth = NULL;
|
||||||
curve25519_secret_key_t *client_auht_sk = NULL;
|
curve25519_secret_key_t *client_auht_sk = NULL;
|
||||||
@ -1865,13 +1865,13 @@ hs_client_decode_descriptor(const char *desc_str,
|
|||||||
uint64_t current_time_period = hs_get_time_period_num(0);
|
uint64_t current_time_period = hs_get_time_period_num(0);
|
||||||
hs_build_blinded_pubkey(service_identity_pk, NULL, 0, current_time_period,
|
hs_build_blinded_pubkey(service_identity_pk, NULL, 0, current_time_period,
|
||||||
&blinded_pubkey);
|
&blinded_pubkey);
|
||||||
hs_get_subcredential(service_identity_pk, &blinded_pubkey, subcredential);
|
hs_get_subcredential(service_identity_pk, &blinded_pubkey, &subcredential);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse descriptor */
|
/* Parse descriptor */
|
||||||
ret = hs_desc_decode_descriptor(desc_str, subcredential,
|
ret = hs_desc_decode_descriptor(desc_str, &subcredential,
|
||||||
client_auht_sk, desc);
|
client_auht_sk, desc);
|
||||||
memwipe(subcredential, 0, sizeof(subcredential));
|
memwipe(&subcredential, 0, sizeof(subcredential));
|
||||||
if (ret != HS_DESC_DECODE_OK) {
|
if (ret != HS_DESC_DECODE_OK) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -2486,4 +2486,3 @@ set_hs_client_auths_map(digest256map_t *map)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif /* defined(TOR_UNIT_TESTS) */
|
#endif /* defined(TOR_UNIT_TESTS) */
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "feature/hs/hs_client.h"
|
#include "feature/hs/hs_client.h"
|
||||||
#include "feature/hs/hs_common.h"
|
#include "feature/hs/hs_common.h"
|
||||||
#include "feature/hs/hs_dos.h"
|
#include "feature/hs/hs_dos.h"
|
||||||
|
#include "feature/hs/hs_ob.h"
|
||||||
#include "feature/hs/hs_ident.h"
|
#include "feature/hs/hs_ident.h"
|
||||||
#include "feature/hs/hs_service.h"
|
#include "feature/hs/hs_service.h"
|
||||||
#include "feature/hs_common/shared_random_client.h"
|
#include "feature/hs_common/shared_random_client.h"
|
||||||
@ -808,12 +809,12 @@ hs_parse_address_impl(const char *address, ed25519_public_key_t *key_out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Using the given identity public key and a blinded public key, compute the
|
/** Using the given identity public key and a blinded public key, compute the
|
||||||
* subcredential and put it in subcred_out (must be of size DIGEST256_LEN).
|
* subcredential and put it in subcred_out.
|
||||||
* This can't fail. */
|
* This can't fail. */
|
||||||
void
|
void
|
||||||
hs_get_subcredential(const ed25519_public_key_t *identity_pk,
|
hs_get_subcredential(const ed25519_public_key_t *identity_pk,
|
||||||
const ed25519_public_key_t *blinded_pk,
|
const ed25519_public_key_t *blinded_pk,
|
||||||
uint8_t *subcred_out)
|
hs_subcredential_t *subcred_out)
|
||||||
{
|
{
|
||||||
uint8_t credential[DIGEST256_LEN];
|
uint8_t credential[DIGEST256_LEN];
|
||||||
crypto_digest_t *digest;
|
crypto_digest_t *digest;
|
||||||
@ -841,7 +842,8 @@ hs_get_subcredential(const ed25519_public_key_t *identity_pk,
|
|||||||
sizeof(credential));
|
sizeof(credential));
|
||||||
crypto_digest_add_bytes(digest, (const char *) blinded_pk->pubkey,
|
crypto_digest_add_bytes(digest, (const char *) blinded_pk->pubkey,
|
||||||
ED25519_PUBKEY_LEN);
|
ED25519_PUBKEY_LEN);
|
||||||
crypto_digest_get_digest(digest, (char *) subcred_out, DIGEST256_LEN);
|
crypto_digest_get_digest(digest, (char *) subcred_out->subcred,
|
||||||
|
SUBCRED_LEN);
|
||||||
crypto_digest_free(digest);
|
crypto_digest_free(digest);
|
||||||
|
|
||||||
memwipe(credential, 0, sizeof(credential));
|
memwipe(credential, 0, sizeof(credential));
|
||||||
@ -909,30 +911,35 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn)
|
|||||||
* case the caller would want only one field. checksum_out MUST at least be 2
|
* case the caller would want only one field. checksum_out MUST at least be 2
|
||||||
* bytes long.
|
* bytes long.
|
||||||
*
|
*
|
||||||
* Return 0 if parsing went well; return -1 in case of error. */
|
* Return 0 if parsing went well; return -1 in case of error and if errmsg is
|
||||||
|
* non NULL, a human readable string message is set. */
|
||||||
int
|
int
|
||||||
hs_parse_address(const char *address, ed25519_public_key_t *key_out,
|
hs_parse_address_no_log(const char *address, ed25519_public_key_t *key_out,
|
||||||
uint8_t *checksum_out, uint8_t *version_out)
|
uint8_t *checksum_out, uint8_t *version_out,
|
||||||
|
const char **errmsg)
|
||||||
{
|
{
|
||||||
char decoded[HS_SERVICE_ADDR_LEN];
|
char decoded[HS_SERVICE_ADDR_LEN];
|
||||||
|
|
||||||
tor_assert(address);
|
tor_assert(address);
|
||||||
|
|
||||||
|
if (errmsg) {
|
||||||
|
*errmsg = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Obvious length check. */
|
/* Obvious length check. */
|
||||||
if (strlen(address) != HS_SERVICE_ADDR_LEN_BASE32) {
|
if (strlen(address) != HS_SERVICE_ADDR_LEN_BASE32) {
|
||||||
log_warn(LD_REND, "Service address %s has an invalid length. "
|
if (errmsg) {
|
||||||
"Expected %lu but got %lu.",
|
*errmsg = "Invalid length";
|
||||||
escaped_safe_str(address),
|
}
|
||||||
(unsigned long) HS_SERVICE_ADDR_LEN_BASE32,
|
|
||||||
(unsigned long) strlen(address));
|
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decode address so we can extract needed fields. */
|
/* Decode address so we can extract needed fields. */
|
||||||
if (base32_decode(decoded, sizeof(decoded), address, strlen(address))
|
if (base32_decode(decoded, sizeof(decoded), address, strlen(address))
|
||||||
!= sizeof(decoded)) {
|
!= sizeof(decoded)) {
|
||||||
log_warn(LD_REND, "Service address %s can't be decoded.",
|
if (errmsg) {
|
||||||
escaped_safe_str(address));
|
*errmsg = "Unable to base32 decode";
|
||||||
|
}
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,6 +951,22 @@ hs_parse_address(const char *address, ed25519_public_key_t *key_out,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Same has hs_parse_address_no_log() but emits a log warning on parsing
|
||||||
|
* failure. */
|
||||||
|
int
|
||||||
|
hs_parse_address(const char *address, ed25519_public_key_t *key_out,
|
||||||
|
uint8_t *checksum_out, uint8_t *version_out)
|
||||||
|
{
|
||||||
|
const char *errmsg = NULL;
|
||||||
|
int ret = hs_parse_address_no_log(address, key_out, checksum_out,
|
||||||
|
version_out, &errmsg);
|
||||||
|
if (ret < 0) {
|
||||||
|
log_warn(LD_REND, "Service address %s failed to be parsed: %s",
|
||||||
|
escaped_safe_str(address), errmsg);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/** Validate a given onion address. The length, the base32 decoding, and
|
/** Validate a given onion address. The length, the base32 decoding, and
|
||||||
* checksum are validated. Return 1 if valid else 0. */
|
* checksum are validated. Return 1 if valid else 0. */
|
||||||
int
|
int
|
||||||
@ -1807,6 +1830,7 @@ hs_free_all(void)
|
|||||||
hs_service_free_all();
|
hs_service_free_all();
|
||||||
hs_cache_free_all();
|
hs_cache_free_all();
|
||||||
hs_client_free_all();
|
hs_client_free_all();
|
||||||
|
hs_ob_free_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For the given origin circuit circ, decrement the number of rendezvous
|
/** For the given origin circuit circ, decrement the number of rendezvous
|
||||||
|
@ -179,6 +179,10 @@ void hs_build_address(const struct ed25519_public_key_t *key, uint8_t version,
|
|||||||
int hs_address_is_valid(const char *address);
|
int hs_address_is_valid(const char *address);
|
||||||
int hs_parse_address(const char *address, struct ed25519_public_key_t *key_out,
|
int hs_parse_address(const char *address, struct ed25519_public_key_t *key_out,
|
||||||
uint8_t *checksum_out, uint8_t *version_out);
|
uint8_t *checksum_out, uint8_t *version_out);
|
||||||
|
int hs_parse_address_no_log(const char *address,
|
||||||
|
struct ed25519_public_key_t *key_out,
|
||||||
|
uint8_t *checksum_out, uint8_t *version_out,
|
||||||
|
const char **errmsg);
|
||||||
|
|
||||||
void hs_build_blinded_pubkey(const struct ed25519_public_key_t *pubkey,
|
void hs_build_blinded_pubkey(const struct ed25519_public_key_t *pubkey,
|
||||||
const uint8_t *secret, size_t secret_len,
|
const uint8_t *secret, size_t secret_len,
|
||||||
@ -210,9 +214,10 @@ const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data,
|
|||||||
|
|
||||||
routerstatus_t *pick_hsdir(const char *desc_id, const char *desc_id_base32);
|
routerstatus_t *pick_hsdir(const char *desc_id, const char *desc_id_base32);
|
||||||
|
|
||||||
|
struct hs_subcredential_t;
|
||||||
void hs_get_subcredential(const struct ed25519_public_key_t *identity_pk,
|
void hs_get_subcredential(const struct ed25519_public_key_t *identity_pk,
|
||||||
const struct ed25519_public_key_t *blinded_pk,
|
const struct ed25519_public_key_t *blinded_pk,
|
||||||
uint8_t *subcred_out);
|
struct hs_subcredential_t *subcred_out);
|
||||||
|
|
||||||
uint64_t hs_get_previous_time_period_num(time_t now);
|
uint64_t hs_get_previous_time_period_num(time_t now);
|
||||||
uint64_t hs_get_time_period_num(time_t now);
|
uint64_t hs_get_time_period_num(time_t now);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "feature/hs/hs_common.h"
|
#include "feature/hs/hs_common.h"
|
||||||
#include "feature/hs/hs_config.h"
|
#include "feature/hs/hs_config.h"
|
||||||
#include "feature/hs/hs_client.h"
|
#include "feature/hs/hs_client.h"
|
||||||
|
#include "feature/hs/hs_ob.h"
|
||||||
#include "feature/hs/hs_service.h"
|
#include "feature/hs/hs_service.h"
|
||||||
#include "feature/rend/rendclient.h"
|
#include "feature/rend/rendclient.h"
|
||||||
#include "feature/rend/rendservice.h"
|
#include "feature/rend/rendservice.h"
|
||||||
@ -219,6 +220,7 @@ config_has_invalid_options(const config_line_t *line_,
|
|||||||
"HiddenServiceEnableIntroDoSDefense",
|
"HiddenServiceEnableIntroDoSDefense",
|
||||||
"HiddenServiceEnableIntroDoSRatePerSec",
|
"HiddenServiceEnableIntroDoSRatePerSec",
|
||||||
"HiddenServiceEnableIntroDoSBurstPerSec",
|
"HiddenServiceEnableIntroDoSBurstPerSec",
|
||||||
|
"HiddenServiceOnionBalanceInstance",
|
||||||
NULL /* End marker. */
|
NULL /* End marker. */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -317,7 +319,7 @@ config_service_v3(const config_line_t *line_,
|
|||||||
int have_num_ip = 0;
|
int have_num_ip = 0;
|
||||||
bool export_circuit_id = false; /* just to detect duplicate options */
|
bool export_circuit_id = false; /* just to detect duplicate options */
|
||||||
bool dos_enabled = false, dos_rate_per_sec = false;
|
bool dos_enabled = false, dos_rate_per_sec = false;
|
||||||
bool dos_burst_per_sec = false;
|
bool dos_burst_per_sec = false, ob_instance = false;
|
||||||
const char *dup_opt_seen = NULL;
|
const char *dup_opt_seen = NULL;
|
||||||
const config_line_t *line;
|
const config_line_t *line;
|
||||||
|
|
||||||
@ -402,6 +404,27 @@ config_service_v3(const config_line_t *line_,
|
|||||||
config->intro_dos_burst_per_sec);
|
config->intro_dos_burst_per_sec);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcasecmp(line->key, "HiddenServiceOnionBalanceInstance")) {
|
||||||
|
bool enabled = !!helper_parse_uint64(line->key, line->value,
|
||||||
|
0, 1, &ok);
|
||||||
|
if (!ok || ob_instance) {
|
||||||
|
if (ob_instance) {
|
||||||
|
dup_opt_seen = line->key;
|
||||||
|
}
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
ob_instance = true;
|
||||||
|
if (!enabled) {
|
||||||
|
/* Skip if this is disabled. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Option is enabled, parse config file. */
|
||||||
|
ok = hs_ob_parse_config_file(config);
|
||||||
|
if (!ok) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We do not load the key material for the service at this stage. This is
|
/* We do not load the key material for the service at this stage. This is
|
||||||
|
@ -211,7 +211,7 @@ build_secret_input(const hs_descriptor_t *desc,
|
|||||||
memcpy(secret_input, secret_data, secret_data_len);
|
memcpy(secret_input, secret_data, secret_data_len);
|
||||||
offset += secret_data_len;
|
offset += secret_data_len;
|
||||||
/* Copy subcredential. */
|
/* Copy subcredential. */
|
||||||
memcpy(secret_input + offset, desc->subcredential, DIGEST256_LEN);
|
memcpy(secret_input + offset, desc->subcredential.subcred, DIGEST256_LEN);
|
||||||
offset += DIGEST256_LEN;
|
offset += DIGEST256_LEN;
|
||||||
/* Copy revision counter value. */
|
/* Copy revision counter value. */
|
||||||
set_uint64(secret_input + offset,
|
set_uint64(secret_input + offset,
|
||||||
@ -1018,10 +1018,6 @@ desc_encode_v3(const hs_descriptor_t *desc,
|
|||||||
tor_assert(encoded_out);
|
tor_assert(encoded_out);
|
||||||
tor_assert(desc->plaintext_data.version == 3);
|
tor_assert(desc->plaintext_data.version == 3);
|
||||||
|
|
||||||
if (BUG(desc->subcredential == NULL)) {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build the non-encrypted values. */
|
/* Build the non-encrypted values. */
|
||||||
{
|
{
|
||||||
char *encoded_cert;
|
char *encoded_cert;
|
||||||
@ -1366,8 +1362,7 @@ encrypted_data_length_is_valid(size_t len)
|
|||||||
* and return the buffer's length. The caller should wipe and free its content
|
* and return the buffer's length. The caller should wipe and free its content
|
||||||
* once done with it. This function can't fail. */
|
* once done with it. This function can't fail. */
|
||||||
static size_t
|
static size_t
|
||||||
build_descriptor_cookie_keys(const uint8_t *subcredential,
|
build_descriptor_cookie_keys(const hs_subcredential_t *subcredential,
|
||||||
size_t subcredential_len,
|
|
||||||
const curve25519_secret_key_t *sk,
|
const curve25519_secret_key_t *sk,
|
||||||
const curve25519_public_key_t *pk,
|
const curve25519_public_key_t *pk,
|
||||||
uint8_t **keys_out)
|
uint8_t **keys_out)
|
||||||
@ -1389,7 +1384,7 @@ build_descriptor_cookie_keys(const uint8_t *subcredential,
|
|||||||
|
|
||||||
/* Calculate KEYS = KDF(subcredential | SECRET_SEED, 40) */
|
/* Calculate KEYS = KDF(subcredential | SECRET_SEED, 40) */
|
||||||
xof = crypto_xof_new();
|
xof = crypto_xof_new();
|
||||||
crypto_xof_add_bytes(xof, subcredential, subcredential_len);
|
crypto_xof_add_bytes(xof, subcredential->subcred, SUBCRED_LEN);
|
||||||
crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed));
|
crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed));
|
||||||
crypto_xof_squeeze_bytes(xof, keystream, keystream_len);
|
crypto_xof_squeeze_bytes(xof, keystream, keystream_len);
|
||||||
crypto_xof_free(xof);
|
crypto_xof_free(xof);
|
||||||
@ -1426,11 +1421,12 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
|
|||||||
sizeof(desc->superencrypted_data.auth_ephemeral_pubkey)));
|
sizeof(desc->superencrypted_data.auth_ephemeral_pubkey)));
|
||||||
tor_assert(!fast_mem_is_zero((char *) client_auth_sk,
|
tor_assert(!fast_mem_is_zero((char *) client_auth_sk,
|
||||||
sizeof(*client_auth_sk)));
|
sizeof(*client_auth_sk)));
|
||||||
tor_assert(!fast_mem_is_zero((char *) desc->subcredential, DIGEST256_LEN));
|
tor_assert(!fast_mem_is_zero((char *) desc->subcredential.subcred,
|
||||||
|
DIGEST256_LEN));
|
||||||
|
|
||||||
/* Get the KEYS component to derive the CLIENT-ID and COOKIE-KEY. */
|
/* Get the KEYS component to derive the CLIENT-ID and COOKIE-KEY. */
|
||||||
keystream_length =
|
keystream_length =
|
||||||
build_descriptor_cookie_keys(desc->subcredential, DIGEST256_LEN,
|
build_descriptor_cookie_keys(&desc->subcredential,
|
||||||
client_auth_sk,
|
client_auth_sk,
|
||||||
&desc->superencrypted_data.auth_ephemeral_pubkey,
|
&desc->superencrypted_data.auth_ephemeral_pubkey,
|
||||||
&keystream);
|
&keystream);
|
||||||
@ -2558,7 +2554,7 @@ hs_desc_decode_plaintext(const char *encoded,
|
|||||||
* set to NULL. */
|
* set to NULL. */
|
||||||
hs_desc_decode_status_t
|
hs_desc_decode_status_t
|
||||||
hs_desc_decode_descriptor(const char *encoded,
|
hs_desc_decode_descriptor(const char *encoded,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
const curve25519_secret_key_t *client_auth_sk,
|
const curve25519_secret_key_t *client_auth_sk,
|
||||||
hs_descriptor_t **desc_out)
|
hs_descriptor_t **desc_out)
|
||||||
{
|
{
|
||||||
@ -2576,7 +2572,7 @@ hs_desc_decode_descriptor(const char *encoded,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(desc->subcredential, subcredential, sizeof(desc->subcredential));
|
memcpy(&desc->subcredential, subcredential, sizeof(desc->subcredential));
|
||||||
|
|
||||||
ret = hs_desc_decode_plaintext(encoded, &desc->plaintext_data);
|
ret = hs_desc_decode_plaintext(encoded, &desc->plaintext_data);
|
||||||
if (ret != HS_DESC_DECODE_OK) {
|
if (ret != HS_DESC_DECODE_OK) {
|
||||||
@ -2666,7 +2662,7 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
|
|||||||
* symmetric only if the client auth is disabled. That is, the descriptor
|
* symmetric only if the client auth is disabled. That is, the descriptor
|
||||||
* cookie will be NULL. */
|
* cookie will be NULL. */
|
||||||
if (!descriptor_cookie) {
|
if (!descriptor_cookie) {
|
||||||
ret = hs_desc_decode_descriptor(*encoded_out, desc->subcredential,
|
ret = hs_desc_decode_descriptor(*encoded_out, &desc->subcredential,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
if (BUG(ret != HS_DESC_DECODE_OK)) {
|
if (BUG(ret != HS_DESC_DECODE_OK)) {
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@ -2870,7 +2866,7 @@ hs_desc_build_fake_authorized_client(void)
|
|||||||
* key, and descriptor cookie, build the auth client so we can then encode the
|
* key, and descriptor cookie, build the auth client so we can then encode the
|
||||||
* descriptor for publication. client_out must be already allocated. */
|
* descriptor for publication. client_out must be already allocated. */
|
||||||
void
|
void
|
||||||
hs_desc_build_authorized_client(const uint8_t *subcredential,
|
hs_desc_build_authorized_client(const hs_subcredential_t *subcredential,
|
||||||
const curve25519_public_key_t *client_auth_pk,
|
const curve25519_public_key_t *client_auth_pk,
|
||||||
const curve25519_secret_key_t *
|
const curve25519_secret_key_t *
|
||||||
auth_ephemeral_sk,
|
auth_ephemeral_sk,
|
||||||
@ -2898,7 +2894,7 @@ hs_desc_build_authorized_client(const uint8_t *subcredential,
|
|||||||
|
|
||||||
/* Get the KEYS part so we can derive the CLIENT-ID and COOKIE-KEY. */
|
/* Get the KEYS part so we can derive the CLIENT-ID and COOKIE-KEY. */
|
||||||
keystream_length =
|
keystream_length =
|
||||||
build_descriptor_cookie_keys(subcredential, DIGEST256_LEN,
|
build_descriptor_cookie_keys(subcredential,
|
||||||
auth_ephemeral_sk, client_auth_pk,
|
auth_ephemeral_sk, client_auth_pk,
|
||||||
&keystream);
|
&keystream);
|
||||||
tor_assert(keystream_length > 0);
|
tor_assert(keystream_length > 0);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "core/or/or.h"
|
#include "core/or/or.h"
|
||||||
#include "trunnel/ed25519_cert.h" /* needed for trunnel */
|
#include "trunnel/ed25519_cert.h" /* needed for trunnel */
|
||||||
#include "feature/nodelist/torcert.h"
|
#include "feature/nodelist/torcert.h"
|
||||||
|
#include "core/crypto/hs_ntor.h" /* for hs_subcredential_t */
|
||||||
|
|
||||||
/* Trunnel */
|
/* Trunnel */
|
||||||
struct link_specifier_t;
|
struct link_specifier_t;
|
||||||
@ -238,7 +239,7 @@ typedef struct hs_descriptor_t {
|
|||||||
|
|
||||||
/** Subcredentials of a service, used by the client and service to decrypt
|
/** Subcredentials of a service, used by the client and service to decrypt
|
||||||
* the encrypted data. */
|
* the encrypted data. */
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
} hs_descriptor_t;
|
} hs_descriptor_t;
|
||||||
|
|
||||||
/** Return true iff the given descriptor format version is supported. */
|
/** Return true iff the given descriptor format version is supported. */
|
||||||
@ -277,7 +278,7 @@ MOCK_DECL(int,
|
|||||||
char **encoded_out));
|
char **encoded_out));
|
||||||
|
|
||||||
int hs_desc_decode_descriptor(const char *encoded,
|
int hs_desc_decode_descriptor(const char *encoded,
|
||||||
const uint8_t *subcredential,
|
const hs_subcredential_t *subcredential,
|
||||||
const curve25519_secret_key_t *client_auth_sk,
|
const curve25519_secret_key_t *client_auth_sk,
|
||||||
hs_descriptor_t **desc_out);
|
hs_descriptor_t **desc_out);
|
||||||
int hs_desc_decode_plaintext(const char *encoded,
|
int hs_desc_decode_plaintext(const char *encoded,
|
||||||
@ -302,7 +303,7 @@ void hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client);
|
|||||||
|
|
||||||
hs_desc_authorized_client_t *hs_desc_build_fake_authorized_client(void);
|
hs_desc_authorized_client_t *hs_desc_build_fake_authorized_client(void);
|
||||||
|
|
||||||
void hs_desc_build_authorized_client(const uint8_t *subcredential,
|
void hs_desc_build_authorized_client(const hs_subcredential_t *subcredential,
|
||||||
const curve25519_public_key_t *
|
const curve25519_public_key_t *
|
||||||
client_auth_pk,
|
client_auth_pk,
|
||||||
const curve25519_secret_key_t *
|
const curve25519_secret_key_t *
|
||||||
|
408
src/feature/hs/hs_ob.c
Normal file
408
src/feature/hs/hs_ob.c
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
/* Copyright (c) 2017-2020, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file hs_ob.c
|
||||||
|
* \brief Implement Onion Balance specific code.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#define HS_OB_PRIVATE
|
||||||
|
|
||||||
|
#include "feature/hs/hs_service.h"
|
||||||
|
|
||||||
|
#include "feature/nodelist/networkstatus.h"
|
||||||
|
#include "feature/nodelist/networkstatus_st.h"
|
||||||
|
|
||||||
|
#include "lib/confmgt/confmgt.h"
|
||||||
|
#include "lib/encoding/confline.h"
|
||||||
|
|
||||||
|
#include "hs_ob.h"
|
||||||
|
|
||||||
|
/* Options config magic number. */
|
||||||
|
#define OB_OPTIONS_MAGIC 0x631DE7EA
|
||||||
|
|
||||||
|
/* Helper macros. */
|
||||||
|
#define VAR(varname, conftype, member, initvalue) \
|
||||||
|
CONFIG_VAR_ETYPE(ob_options_t, varname, conftype, member, 0, initvalue)
|
||||||
|
#define V(member,conftype,initvalue) \
|
||||||
|
VAR(#member, conftype, member, initvalue)
|
||||||
|
|
||||||
|
/* Dummy instance of ob_options_t, used for type-checking its members with
|
||||||
|
* CONF_CHECK_VAR_TYPE. */
|
||||||
|
DUMMY_TYPECHECK_INSTANCE(ob_options_t);
|
||||||
|
|
||||||
|
/* Array of variables for the config file options. */
|
||||||
|
static const config_var_t config_vars[] = {
|
||||||
|
V(MasterOnionAddress, LINELIST, NULL),
|
||||||
|
|
||||||
|
END_OF_CONFIG_VARS
|
||||||
|
};
|
||||||
|
|
||||||
|
/* "Extra" variable in the state that receives lines we can't parse. This
|
||||||
|
* lets us preserve options from versions of Tor newer than us. */
|
||||||
|
static const struct_member_t config_extra_vars = {
|
||||||
|
.name = "__extra",
|
||||||
|
.type = CONFIG_TYPE_LINELIST,
|
||||||
|
.offset = offsetof(ob_options_t, ExtraLines),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Configuration format of ob_options_t. */
|
||||||
|
static const config_format_t config_format = {
|
||||||
|
.size = sizeof(ob_options_t),
|
||||||
|
.magic = {
|
||||||
|
"ob_options_t",
|
||||||
|
OB_OPTIONS_MAGIC,
|
||||||
|
offsetof(ob_options_t, magic_),
|
||||||
|
},
|
||||||
|
.vars = config_vars,
|
||||||
|
.extra = &config_extra_vars,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Global configuration manager for the config file. */
|
||||||
|
static config_mgr_t *config_options_mgr = NULL;
|
||||||
|
|
||||||
|
/* Return the configuration manager for the config file. */
|
||||||
|
static const config_mgr_t *
|
||||||
|
get_config_options_mgr(void)
|
||||||
|
{
|
||||||
|
if (PREDICT_UNLIKELY(config_options_mgr == NULL)) {
|
||||||
|
config_options_mgr = config_mgr_new(&config_format);
|
||||||
|
config_mgr_freeze(config_options_mgr);
|
||||||
|
}
|
||||||
|
return config_options_mgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ob_option_free(val) \
|
||||||
|
FREE_AND_NULL(ob_options_t, ob_option_free_, (val))
|
||||||
|
|
||||||
|
/** Helper: Free a config options object. */
|
||||||
|
static void
|
||||||
|
ob_option_free_(ob_options_t *opts)
|
||||||
|
{
|
||||||
|
if (opts == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config_free(get_config_options_mgr(), opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return an allocated config options object. */
|
||||||
|
static ob_options_t *
|
||||||
|
ob_option_new(void)
|
||||||
|
{
|
||||||
|
ob_options_t *opts = config_new(get_config_options_mgr());
|
||||||
|
config_init(get_config_options_mgr(), opts);
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper function: From the configuration line value which is an onion
|
||||||
|
* address with the ".onion" extension, find the public key and put it in
|
||||||
|
* pkey_out.
|
||||||
|
*
|
||||||
|
* On success, true is returned. Else, false and pkey is untouched. */
|
||||||
|
static bool
|
||||||
|
get_onion_public_key(const char *value, ed25519_public_key_t *pkey_out)
|
||||||
|
{
|
||||||
|
char address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||||
|
|
||||||
|
tor_assert(value);
|
||||||
|
tor_assert(pkey_out);
|
||||||
|
|
||||||
|
if (strcmpend(value, ".onion")) {
|
||||||
|
/* Not a .onion extension, bad format. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Length validation. The -1 is because sizeof() counts the NUL byte. */
|
||||||
|
if (strlen(value) >
|
||||||
|
(HS_SERVICE_ADDR_LEN_BASE32 + sizeof(".onion") - 1)) {
|
||||||
|
/* Too long, bad format. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't want the .onion so we add 2 because size - 1 is copied with
|
||||||
|
* strlcpy() in order to accomodate the NUL byte and sizeof() counts the NUL
|
||||||
|
* byte so we need to remove them from the equation. */
|
||||||
|
strlcpy(address, value, strlen(value) - sizeof(".onion") + 2);
|
||||||
|
|
||||||
|
if (hs_parse_address_no_log(address, pkey_out, NULL, NULL, NULL) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse the given ob options in opts and set the service config object
|
||||||
|
* accordingly.
|
||||||
|
*
|
||||||
|
* Return 1 on success else 0. */
|
||||||
|
static int
|
||||||
|
ob_option_parse(hs_service_config_t *config, const ob_options_t *opts)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
config_line_t *line;
|
||||||
|
|
||||||
|
tor_assert(config);
|
||||||
|
tor_assert(opts);
|
||||||
|
|
||||||
|
for (line = opts->MasterOnionAddress; line; line = line->next) {
|
||||||
|
/* Allocate config list if need be. */
|
||||||
|
if (!config->ob_master_pubkeys) {
|
||||||
|
config->ob_master_pubkeys = smartlist_new();
|
||||||
|
}
|
||||||
|
ed25519_public_key_t *pubkey = tor_malloc_zero(sizeof(*pubkey));
|
||||||
|
|
||||||
|
if (!get_onion_public_key(line->value, pubkey)) {
|
||||||
|
log_warn(LD_REND, "OnionBalance: MasterOnionAddress %s is invalid",
|
||||||
|
line->value);
|
||||||
|
tor_free(pubkey);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
smartlist_add(config->ob_master_pubkeys, pubkey);
|
||||||
|
log_info(LD_REND, "OnionBalance: MasterOnionAddress %s registered",
|
||||||
|
line->value);
|
||||||
|
}
|
||||||
|
/* Success. */
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
end:
|
||||||
|
/* No keys added, we free the list since no list means no onion balance
|
||||||
|
* support for this tor instance. */
|
||||||
|
if (smartlist_len(config->ob_master_pubkeys) == 0) {
|
||||||
|
smartlist_free(config->ob_master_pubkeys);
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
hs_subcredential_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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Return true iff the given service is configured as an onion balance
|
||||||
|
* instance. To satisfy that condition, there must at least be one master
|
||||||
|
* ed25519 public key configured. */
|
||||||
|
bool
|
||||||
|
hs_ob_service_is_instance(const hs_service_t *service)
|
||||||
|
{
|
||||||
|
if (BUG(service == NULL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No list, we are not an instance. */
|
||||||
|
if (!service->config.ob_master_pubkeys) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return smartlist_len(service->config.ob_master_pubkeys) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read and parse the config file at fname on disk. The service config object
|
||||||
|
* is populated with the options if any.
|
||||||
|
*
|
||||||
|
* Return 1 on success else 0. This is to follow the "ok" convention in
|
||||||
|
* hs_config.c. */
|
||||||
|
int
|
||||||
|
hs_ob_parse_config_file(hs_service_config_t *config)
|
||||||
|
{
|
||||||
|
static const char *fname = "ob_config";
|
||||||
|
int ret = 0;
|
||||||
|
char *content = NULL, *errmsg = NULL, *config_file_path = NULL;
|
||||||
|
ob_options_t *options = NULL;
|
||||||
|
config_line_t *lines = NULL;
|
||||||
|
|
||||||
|
tor_assert(config);
|
||||||
|
|
||||||
|
/* Read file from disk. */
|
||||||
|
config_file_path = hs_path_from_filename(config->directory_path, fname);
|
||||||
|
content = read_file_to_str(config_file_path, 0, NULL);
|
||||||
|
if (!content) {
|
||||||
|
log_warn(LD_FS, "OnionBalance: Unable to read config file %s",
|
||||||
|
escaped(config_file_path));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse lines. */
|
||||||
|
if (config_get_lines(content, &lines, 0) < 0) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
options = ob_option_new();
|
||||||
|
config_assign(get_config_options_mgr(), options, lines, 0, &errmsg);
|
||||||
|
if (errmsg) {
|
||||||
|
log_warn(LD_REND, "OnionBalance: Unable to parse config file: %s",
|
||||||
|
errmsg);
|
||||||
|
tor_free(errmsg);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the options and set the service config object with the details. */
|
||||||
|
ret = ob_option_parse(config, options);
|
||||||
|
|
||||||
|
end:
|
||||||
|
config_free_lines(lines);
|
||||||
|
ob_option_free(options);
|
||||||
|
tor_free(content);
|
||||||
|
tor_free(config_file_path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Compute all possible subcredentials for every onion master key in the given
|
||||||
|
* service config object. subcredentials_out 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_out is set to NULL.
|
||||||
|
*
|
||||||
|
* Otherwise, this can't fail. */
|
||||||
|
STATIC size_t
|
||||||
|
compute_subcredentials(const hs_service_t *service,
|
||||||
|
hs_subcredential_t **subcredentials_out)
|
||||||
|
{
|
||||||
|
unsigned int num_pkeys, idx = 0;
|
||||||
|
hs_subcredential_t *subcreds = NULL;
|
||||||
|
const int steps[3] = {0, -1, 1};
|
||||||
|
const unsigned int num_steps = ARRAY_LENGTH(steps);
|
||||||
|
const uint64_t tp = hs_get_time_period_num(0);
|
||||||
|
|
||||||
|
tor_assert(service);
|
||||||
|
tor_assert(subcredentials_out);
|
||||||
|
/* Our caller has checked these too */
|
||||||
|
tor_assert(service->desc_current);
|
||||||
|
tor_assert(service->desc_next);
|
||||||
|
|
||||||
|
/* Make sure we are an OB instance, or bail out. */
|
||||||
|
num_pkeys = smartlist_len(service->config.ob_master_pubkeys);
|
||||||
|
if (!num_pkeys) {
|
||||||
|
*subcredentials_out = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Time to build all the subcredentials for each time period: two for each
|
||||||
|
* instance descriptor plus three for the onionbalance frontend service: 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.
|
||||||
|
*
|
||||||
|
* 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. Then in the end we also add two
|
||||||
|
* subcredentials for this instance's active descriptors. */
|
||||||
|
subcreds =
|
||||||
|
tor_calloc((num_steps * num_pkeys) + 2, sizeof(hs_subcredential_t));
|
||||||
|
|
||||||
|
/* For each master pubkey we add 3 subcredentials: */
|
||||||
|
for (unsigned int i = 0; i < num_steps; i++) {
|
||||||
|
SMARTLIST_FOREACH_BEGIN(service->config.ob_master_pubkeys,
|
||||||
|
const ed25519_public_key_t *, pkey) {
|
||||||
|
build_subcredential(pkey, tp + steps[i], &subcreds[idx]);
|
||||||
|
idx++;
|
||||||
|
} SMARTLIST_FOREACH_END(pkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And then in the end we add the two subcredentials of the current active
|
||||||
|
* instance descriptors */
|
||||||
|
memcpy(&subcreds[idx++], &service->desc_current->desc->subcredential,
|
||||||
|
sizeof(hs_subcredential_t));
|
||||||
|
memcpy(&subcreds[idx++], &service->desc_next->desc->subcredential,
|
||||||
|
sizeof(hs_subcredential_t));
|
||||||
|
|
||||||
|
log_info(LD_REND, "Refreshing %u onionbalance keys (TP #%d).",
|
||||||
|
idx, (int)tp);
|
||||||
|
|
||||||
|
*subcredentials_out = subcreds;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we are an Onionbalance instance, refresh our keys.
|
||||||
|
*
|
||||||
|
* If we are not an Onionbalance instance or we are not ready to do so, this
|
||||||
|
* is a NOP.
|
||||||
|
*
|
||||||
|
* This function is called everytime we build a new descriptor. That's because
|
||||||
|
* we want our Onionbalance keys to always use up-to-date subcredentials both
|
||||||
|
* for the instance (ourselves) and for the onionbalance frontend.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hs_ob_refresh_keys(hs_service_t *service)
|
||||||
|
{
|
||||||
|
hs_subcredential_t *ob_subcreds = NULL;
|
||||||
|
size_t num_subcreds;
|
||||||
|
|
||||||
|
tor_assert(service);
|
||||||
|
|
||||||
|
/* Don't do any of this if we are not configured as an OB instance */
|
||||||
|
if (!hs_ob_service_is_instance(service)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need both service descriptors created to make onionbalance keys.
|
||||||
|
*
|
||||||
|
* That's because we fetch our own (the instance's) subcredentials from our
|
||||||
|
* own descriptors which should always include the latest subcredentials that
|
||||||
|
* clients would use.
|
||||||
|
*
|
||||||
|
* This function is called with each descriptor build, so we will be
|
||||||
|
* eventually be called when both descriptors are created. */
|
||||||
|
if (!service->desc_current || !service->desc_next) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a new set of subcreds */
|
||||||
|
num_subcreds = compute_subcredentials(service, &ob_subcreds);
|
||||||
|
if (BUG(!num_subcreds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete old subcredentials if any */
|
||||||
|
if (service->ob_subcreds) {
|
||||||
|
tor_free(service->ob_subcreds);
|
||||||
|
}
|
||||||
|
|
||||||
|
service->ob_subcreds = ob_subcreds;
|
||||||
|
service->n_ob_subcreds = num_subcreds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Free any memory allocated by the onionblance subsystem. */
|
||||||
|
void
|
||||||
|
hs_ob_free_all(void)
|
||||||
|
{
|
||||||
|
config_mgr_free(config_options_mgr);
|
||||||
|
}
|
40
src/feature/hs/hs_ob.h
Normal file
40
src/feature/hs/hs_ob.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* Copyright (c) 2020, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file hs_ob.h
|
||||||
|
* \brief Header file for the specific code for onion balance.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_HS_OB_H
|
||||||
|
#define TOR_HS_OB_H
|
||||||
|
|
||||||
|
#include "hs_service.h"
|
||||||
|
|
||||||
|
bool hs_ob_service_is_instance(const hs_service_t *service);
|
||||||
|
|
||||||
|
int hs_ob_parse_config_file(hs_service_config_t *config);
|
||||||
|
|
||||||
|
struct hs_subcredential_t;
|
||||||
|
|
||||||
|
void hs_ob_free_all(void);
|
||||||
|
|
||||||
|
void hs_ob_refresh_keys(hs_service_t *service);
|
||||||
|
|
||||||
|
#ifdef HS_OB_PRIVATE
|
||||||
|
|
||||||
|
STATIC size_t compute_subcredentials(const hs_service_t *service,
|
||||||
|
struct hs_subcredential_t **subcredentials);
|
||||||
|
|
||||||
|
typedef struct ob_options_t {
|
||||||
|
/** Magic number to identify the structure in memory. */
|
||||||
|
uint32_t magic_;
|
||||||
|
/** Master Onion Address(es). */
|
||||||
|
struct config_line_t *MasterOnionAddress;
|
||||||
|
/** Extra Lines for configuration we might not know. */
|
||||||
|
struct config_line_t *ExtraLines;
|
||||||
|
} ob_options_t;
|
||||||
|
|
||||||
|
#endif /* defined(HS_OB_PRIVATE) */
|
||||||
|
|
||||||
|
#endif /* !defined(TOR_HS_OB_H) */
|
@ -41,6 +41,7 @@
|
|||||||
#include "feature/hs/hs_intropoint.h"
|
#include "feature/hs/hs_intropoint.h"
|
||||||
#include "feature/hs/hs_service.h"
|
#include "feature/hs/hs_service.h"
|
||||||
#include "feature/hs/hs_stats.h"
|
#include "feature/hs/hs_stats.h"
|
||||||
|
#include "feature/hs/hs_ob.h"
|
||||||
|
|
||||||
#include "feature/dircommon/dir_connection_st.h"
|
#include "feature/dircommon/dir_connection_st.h"
|
||||||
#include "core/or/edge_connection_st.h"
|
#include "core/or/edge_connection_st.h"
|
||||||
@ -267,6 +268,11 @@ service_clear_config(hs_service_config_t *config)
|
|||||||
service_authorized_client_free(p));
|
service_authorized_client_free(p));
|
||||||
smartlist_free(config->clients);
|
smartlist_free(config->clients);
|
||||||
}
|
}
|
||||||
|
if (config->ob_master_pubkeys) {
|
||||||
|
SMARTLIST_FOREACH(config->ob_master_pubkeys, ed25519_public_key_t *, k,
|
||||||
|
tor_free(k));
|
||||||
|
smartlist_free(config->ob_master_pubkeys);
|
||||||
|
}
|
||||||
memset(config, 0, sizeof(*config));
|
memset(config, 0, sizeof(*config));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1764,7 +1770,8 @@ build_service_desc_superencrypted(const hs_service_t *service,
|
|||||||
sizeof(curve25519_public_key_t));
|
sizeof(curve25519_public_key_t));
|
||||||
|
|
||||||
/* Test that subcred is not zero because we might use it below */
|
/* Test that subcred is not zero because we might use it below */
|
||||||
if (BUG(fast_mem_is_zero((char*)desc->desc->subcredential, DIGEST256_LEN))) {
|
if (BUG(fast_mem_is_zero((char*)desc->desc->subcredential.subcred,
|
||||||
|
DIGEST256_LEN))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1781,7 +1788,7 @@ build_service_desc_superencrypted(const hs_service_t *service,
|
|||||||
|
|
||||||
/* Prepare the client for descriptor and then add to the list in the
|
/* Prepare the client for descriptor and then add to the list in the
|
||||||
* superencrypted part of the descriptor */
|
* superencrypted part of the descriptor */
|
||||||
hs_desc_build_authorized_client(desc->desc->subcredential,
|
hs_desc_build_authorized_client(&desc->desc->subcredential,
|
||||||
&client->client_pk,
|
&client->client_pk,
|
||||||
&desc->auth_ephemeral_kp.seckey,
|
&desc->auth_ephemeral_kp.seckey,
|
||||||
desc->descriptor_cookie, desc_client);
|
desc->descriptor_cookie, desc_client);
|
||||||
@ -1837,7 +1844,7 @@ build_service_desc_plaintext(const hs_service_t *service,
|
|||||||
|
|
||||||
/* Set the subcredential. */
|
/* Set the subcredential. */
|
||||||
hs_get_subcredential(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
|
hs_get_subcredential(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
|
||||||
desc->desc->subcredential);
|
&desc->desc->subcredential);
|
||||||
|
|
||||||
plaintext = &desc->desc->plaintext_data;
|
plaintext = &desc->desc->plaintext_data;
|
||||||
|
|
||||||
@ -1980,9 +1987,15 @@ build_service_descriptor(hs_service_t *service, uint64_t time_period_num,
|
|||||||
|
|
||||||
/* Assign newly built descriptor to the next slot. */
|
/* Assign newly built descriptor to the next slot. */
|
||||||
*desc_out = desc;
|
*desc_out = desc;
|
||||||
|
|
||||||
/* Fire a CREATED control port event. */
|
/* Fire a CREATED control port event. */
|
||||||
hs_control_desc_event_created(service->onion_address,
|
hs_control_desc_event_created(service->onion_address,
|
||||||
&desc->blinded_kp.pubkey);
|
&desc->blinded_kp.pubkey);
|
||||||
|
|
||||||
|
/* If we are an onionbalance instance, we refresh our keys when we rotate
|
||||||
|
* descriptors. */
|
||||||
|
hs_ob_refresh_keys(service);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -3369,7 +3382,7 @@ service_handle_introduce2(origin_circuit_t *circ, const uint8_t *payload,
|
|||||||
|
|
||||||
/* The following will parse, decode and launch the rendezvous point circuit.
|
/* The following will parse, decode and launch the rendezvous point circuit.
|
||||||
* Both current and legacy cells are handled. */
|
* Both current and legacy cells are handled. */
|
||||||
if (hs_circ_handle_introduce2(service, circ, ip, desc->desc->subcredential,
|
if (hs_circ_handle_introduce2(service, circ, ip, &desc->desc->subcredential,
|
||||||
payload, payload_len) < 0) {
|
payload, payload_len) < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -4042,6 +4055,11 @@ hs_service_free_(hs_service_t *service)
|
|||||||
replaycache_free(service->state.replay_cache_rend_cookie);
|
replaycache_free(service->state.replay_cache_rend_cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free onionbalance subcredentials (if any) */
|
||||||
|
if (service->ob_subcreds) {
|
||||||
|
tor_free(service->ob_subcreds);
|
||||||
|
}
|
||||||
|
|
||||||
/* Wipe service keys. */
|
/* Wipe service keys. */
|
||||||
memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk));
|
memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk));
|
||||||
|
|
||||||
|
@ -248,10 +248,14 @@ typedef struct hs_service_config_t {
|
|||||||
/** Does this service export the circuit ID of its clients? */
|
/** Does this service export the circuit ID of its clients? */
|
||||||
hs_circuit_id_protocol_t circuit_id_protocol;
|
hs_circuit_id_protocol_t circuit_id_protocol;
|
||||||
|
|
||||||
/* DoS defenses. For the ESTABLISH_INTRO cell extension. */
|
/** DoS defenses. For the ESTABLISH_INTRO cell extension. */
|
||||||
unsigned int has_dos_defense_enabled : 1;
|
unsigned int has_dos_defense_enabled : 1;
|
||||||
uint32_t intro_dos_rate_per_sec;
|
uint32_t intro_dos_rate_per_sec;
|
||||||
uint32_t intro_dos_burst_per_sec;
|
uint32_t intro_dos_burst_per_sec;
|
||||||
|
|
||||||
|
/** If set, contains the Onion Balance master ed25519 public key (taken from
|
||||||
|
* an .onion addresses) that this tor instance serves as backend. */
|
||||||
|
smartlist_t *ob_master_pubkeys;
|
||||||
} hs_service_config_t;
|
} hs_service_config_t;
|
||||||
|
|
||||||
/** Service state. */
|
/** Service state. */
|
||||||
@ -301,8 +305,13 @@ typedef struct hs_service_t {
|
|||||||
/** Next descriptor. */
|
/** Next descriptor. */
|
||||||
hs_service_descriptor_t *desc_next;
|
hs_service_descriptor_t *desc_next;
|
||||||
|
|
||||||
/* XXX: Credential (client auth.) #20700. */
|
/* If this is an onionbalance instance, this is an array of subcredentials
|
||||||
|
* that should be used when decrypting an INTRO2 cell. If this is not an
|
||||||
|
* onionbalance instance, this is NULL.
|
||||||
|
* See [ONIONBALANCE] section in rend-spec-v3.txt for more details . */
|
||||||
|
hs_subcredential_t *ob_subcreds;
|
||||||
|
/* Number of OB subcredentials */
|
||||||
|
size_t n_ob_subcreds;
|
||||||
} hs_service_t;
|
} hs_service_t;
|
||||||
|
|
||||||
/** For the service global hash map, we define a specific type for it which
|
/** For the service global hash map, we define a specific type for it which
|
||||||
|
@ -13,6 +13,7 @@ LIBTOR_APP_A_SOURCES += \
|
|||||||
src/feature/hs/hs_dos.c \
|
src/feature/hs/hs_dos.c \
|
||||||
src/feature/hs/hs_ident.c \
|
src/feature/hs/hs_ident.c \
|
||||||
src/feature/hs/hs_intropoint.c \
|
src/feature/hs/hs_intropoint.c \
|
||||||
|
src/feature/hs/hs_ob.c \
|
||||||
src/feature/hs/hs_service.c \
|
src/feature/hs/hs_service.c \
|
||||||
src/feature/hs/hs_stats.c
|
src/feature/hs/hs_stats.c
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ noinst_HEADERS += \
|
|||||||
src/feature/hs/hs_dos.h \
|
src/feature/hs/hs_dos.h \
|
||||||
src/feature/hs/hs_ident.h \
|
src/feature/hs/hs_ident.h \
|
||||||
src/feature/hs/hs_intropoint.h \
|
src/feature/hs/hs_intropoint.h \
|
||||||
|
src/feature/hs/hs_ob.h \
|
||||||
src/feature/hs/hs_service.h \
|
src/feature/hs/hs_service.h \
|
||||||
src/feature/hs/hs_stats.h \
|
src/feature/hs/hs_stats.h \
|
||||||
src/feature/hs/hsdir_index_st.h
|
src/feature/hs/hsdir_index_st.h
|
||||||
|
@ -1240,8 +1240,8 @@ node_get_rsa_id_digest(const node_t *node)
|
|||||||
* If node is NULL, returns an empty smartlist.
|
* If node is NULL, returns an empty smartlist.
|
||||||
*
|
*
|
||||||
* The smartlist must be freed using link_specifier_smartlist_free(). */
|
* The smartlist must be freed using link_specifier_smartlist_free(). */
|
||||||
smartlist_t *
|
MOCK_IMPL(smartlist_t *,
|
||||||
node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
|
node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn))
|
||||||
{
|
{
|
||||||
link_specifier_t *ls;
|
link_specifier_t *ls;
|
||||||
tor_addr_port_t ap;
|
tor_addr_port_t ap;
|
||||||
|
@ -80,8 +80,8 @@ int node_supports_ed25519_hs_intro(const node_t *node);
|
|||||||
int node_supports_v3_rendezvous_point(const node_t *node);
|
int node_supports_v3_rendezvous_point(const node_t *node);
|
||||||
int node_supports_establish_intro_dos_extension(const node_t *node);
|
int node_supports_establish_intro_dos_extension(const node_t *node);
|
||||||
const uint8_t *node_get_rsa_id_digest(const node_t *node);
|
const uint8_t *node_get_rsa_id_digest(const node_t *node);
|
||||||
smartlist_t *node_get_link_specifier_smartlist(const node_t *node,
|
MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node,
|
||||||
bool direct_conn);
|
bool direct_conn));
|
||||||
void link_specifier_smartlist_free_(smartlist_t *ls_list);
|
void link_specifier_smartlist_free_(smartlist_t *ls_list);
|
||||||
#define link_specifier_smartlist_free(ls_list) \
|
#define link_specifier_smartlist_free(ls_list) \
|
||||||
FREE_AND_NULL(smartlist_t, link_specifier_smartlist_free_, (ls_list))
|
FREE_AND_NULL(smartlist_t, link_specifier_smartlist_free_, (ls_list))
|
||||||
|
@ -279,3 +279,30 @@ select_array_member_cumulative_timei(const uint64_t *entries, int n_entries,
|
|||||||
|
|
||||||
return i_chosen;
|
return i_chosen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If <b>s</b> is true, then copy <b>n</b> bytes from <b>src</b> to
|
||||||
|
* <b>dest</b>. Otherwise leave <b>dest</b> alone.
|
||||||
|
*
|
||||||
|
* This function behaves the same as
|
||||||
|
*
|
||||||
|
* if (s)
|
||||||
|
* memcpy(dest, src, n);
|
||||||
|
*
|
||||||
|
* except that it tries to run in the same amount of time whether <b>s</b> is
|
||||||
|
* true or not.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
memcpy_if_true_timei(bool s, void *dest, const void *src, size_t n)
|
||||||
|
{
|
||||||
|
// If s is true, mask will be ~0. If s is false, mask will be 0.
|
||||||
|
const char mask = (char) -(signed char)s;
|
||||||
|
|
||||||
|
char *destp = dest;
|
||||||
|
const char *srcp = src;
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
*destp = (*destp & ~mask) | (*srcp & mask);
|
||||||
|
++destp;
|
||||||
|
++srcp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -73,4 +73,6 @@ int select_array_member_cumulative_timei(const uint64_t *entries,
|
|||||||
int n_entries,
|
int n_entries,
|
||||||
uint64_t total, uint64_t rand_val);
|
uint64_t total, uint64_t rand_val);
|
||||||
|
|
||||||
|
void memcpy_if_true_timei(bool s, void *dest, const void *src, size_t n);
|
||||||
|
|
||||||
#endif /* !defined(TOR_DI_OPS_H) */
|
#endif /* !defined(TOR_DI_OPS_H) */
|
||||||
|
@ -85,12 +85,12 @@ int
|
|||||||
fuzz_main(const uint8_t *data, size_t sz)
|
fuzz_main(const uint8_t *data, size_t sz)
|
||||||
{
|
{
|
||||||
hs_descriptor_t *desc = NULL;
|
hs_descriptor_t *desc = NULL;
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
|
|
||||||
char *fuzzing_data = tor_memdup_nulterm(data, sz);
|
char *fuzzing_data = tor_memdup_nulterm(data, sz);
|
||||||
memset(subcredential, 'A', sizeof(subcredential));
|
memset(&subcredential, 'A', sizeof(subcredential));
|
||||||
|
|
||||||
hs_desc_decode_descriptor(fuzzing_data, subcredential, NULL, &desc);
|
hs_desc_decode_descriptor(fuzzing_data, &subcredential, NULL, &desc);
|
||||||
if (desc) {
|
if (desc) {
|
||||||
log_debug(LD_GENERAL, "Decoding okay");
|
log_debug(LD_GENERAL, "Decoding okay");
|
||||||
hs_descriptor_free(desc);
|
hs_descriptor_free(desc);
|
||||||
@ -101,4 +101,3 @@ fuzz_main(const uint8_t *data, size_t sz)
|
|||||||
tor_free(fuzzing_data);
|
tor_free(fuzzing_data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,9 +13,22 @@
|
|||||||
#include "feature/hs/hs_service.h"
|
#include "feature/hs/hs_service.h"
|
||||||
#include "test/hs_test_helpers.h"
|
#include "test/hs_test_helpers.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an introduction point taken straight out of an HSv3 descriptor.
|
||||||
|
*
|
||||||
|
* Use 'signing_kp' to sign the introduction point certificates.
|
||||||
|
*
|
||||||
|
* If 'intro_auth_kp' is provided use that as the introduction point
|
||||||
|
* authentication keypair, otherwise generate one on the fly.
|
||||||
|
*
|
||||||
|
* If 'intro_enc_kp' is provided use that as the introduction point encryption
|
||||||
|
* keypair, otherwise generate one on the fly.
|
||||||
|
*/
|
||||||
hs_desc_intro_point_t *
|
hs_desc_intro_point_t *
|
||||||
hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
|
hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
|
||||||
const char *addr, int legacy)
|
const char *addr, int legacy,
|
||||||
|
const ed25519_keypair_t *intro_auth_kp,
|
||||||
|
const curve25519_keypair_t *intro_enc_kp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
ed25519_keypair_t auth_kp;
|
ed25519_keypair_t auth_kp;
|
||||||
@ -56,8 +69,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
|
|||||||
smartlist_add(ip->link_specifiers, ls_ip);
|
smartlist_add(ip->link_specifiers, ls_ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (intro_auth_kp) {
|
||||||
|
memcpy(&auth_kp, intro_auth_kp, sizeof(ed25519_keypair_t));
|
||||||
|
} else {
|
||||||
ret = ed25519_keypair_generate(&auth_kp, 0);
|
ret = ed25519_keypair_generate(&auth_kp, 0);
|
||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
}
|
||||||
ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY,
|
ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY,
|
||||||
&auth_kp.pubkey, now,
|
&auth_kp.pubkey, now,
|
||||||
HS_DESC_CERT_LIFETIME,
|
HS_DESC_CERT_LIFETIME,
|
||||||
@ -85,8 +102,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
|
|||||||
ed25519_keypair_t ed25519_kp;
|
ed25519_keypair_t ed25519_kp;
|
||||||
tor_cert_t *cross_cert;
|
tor_cert_t *cross_cert;
|
||||||
|
|
||||||
|
if (intro_enc_kp) {
|
||||||
|
memcpy(&curve25519_kp, intro_enc_kp, sizeof(curve25519_keypair_t));
|
||||||
|
} else {
|
||||||
ret = curve25519_keypair_generate(&curve25519_kp, 0);
|
ret = curve25519_keypair_generate(&curve25519_kp, 0);
|
||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
}
|
||||||
ed25519_keypair_from_curve25519_keypair(&ed25519_kp, &signbit,
|
ed25519_keypair_from_curve25519_keypair(&ed25519_kp, &signbit,
|
||||||
&curve25519_kp);
|
&curve25519_kp);
|
||||||
cross_cert = tor_cert_create(signing_kp, CERT_TYPE_CROSS_HS_IP_KEYS,
|
cross_cert = tor_cert_create(signing_kp, CERT_TYPE_CROSS_HS_IP_KEYS,
|
||||||
@ -95,6 +116,8 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
|
|||||||
CERT_FLAG_INCLUDE_SIGNING_KEY);
|
CERT_FLAG_INCLUDE_SIGNING_KEY);
|
||||||
tt_assert(cross_cert);
|
tt_assert(cross_cert);
|
||||||
ip->enc_key_cert = cross_cert;
|
ip->enc_key_cert = cross_cert;
|
||||||
|
memcpy(ip->enc_key.public_key, curve25519_kp.pubkey.public_key,
|
||||||
|
CURVE25519_PUBKEY_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
intro_point = ip;
|
intro_point = ip;
|
||||||
@ -140,7 +163,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
|
|||||||
desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
|
desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
|
||||||
|
|
||||||
hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
|
hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
|
||||||
desc->subcredential);
|
&desc->subcredential);
|
||||||
|
|
||||||
/* Setup superencrypted data section. */
|
/* Setup superencrypted data section. */
|
||||||
ret = curve25519_keypair_generate(&auth_ephemeral_kp, 0);
|
ret = curve25519_keypair_generate(&auth_ephemeral_kp, 0);
|
||||||
@ -165,13 +188,17 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
|
|||||||
if (!no_ip) {
|
if (!no_ip) {
|
||||||
/* Add four intro points. */
|
/* Add four intro points. */
|
||||||
smartlist_add(desc->encrypted_data.intro_points,
|
smartlist_add(desc->encrypted_data.intro_points,
|
||||||
hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0));
|
hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0,
|
||||||
|
NULL, NULL));
|
||||||
smartlist_add(desc->encrypted_data.intro_points,
|
smartlist_add(desc->encrypted_data.intro_points,
|
||||||
hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0));
|
hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0,
|
||||||
|
NULL, NULL));
|
||||||
smartlist_add(desc->encrypted_data.intro_points,
|
smartlist_add(desc->encrypted_data.intro_points,
|
||||||
hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1));
|
hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1,
|
||||||
|
NULL, NULL));
|
||||||
smartlist_add(desc->encrypted_data.intro_points,
|
smartlist_add(desc->encrypted_data.intro_points,
|
||||||
hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1));
|
hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1,
|
||||||
|
NULL, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
descp = desc;
|
descp = desc;
|
||||||
@ -186,7 +213,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
|
|||||||
* an HS. Used to decrypt descriptors in unittests. */
|
* an HS. Used to decrypt descriptors in unittests. */
|
||||||
void
|
void
|
||||||
hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
|
hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
|
||||||
uint8_t *subcred_out)
|
hs_subcredential_t *subcred_out)
|
||||||
{
|
{
|
||||||
ed25519_keypair_t blinded_kp;
|
ed25519_keypair_t blinded_kp;
|
||||||
uint64_t current_time_period = hs_get_time_period_num(approx_time());
|
uint64_t current_time_period = hs_get_time_period_num(approx_time());
|
||||||
@ -233,7 +260,7 @@ hs_helper_build_hs_desc_with_client_auth(
|
|||||||
memcpy(&desc->superencrypted_data.auth_ephemeral_pubkey,
|
memcpy(&desc->superencrypted_data.auth_ephemeral_pubkey,
|
||||||
&auth_ephemeral_kp.pubkey, sizeof(curve25519_public_key_t));
|
&auth_ephemeral_kp.pubkey, sizeof(curve25519_public_key_t));
|
||||||
|
|
||||||
hs_desc_build_authorized_client(desc->subcredential, client_pk,
|
hs_desc_build_authorized_client(&desc->subcredential, client_pk,
|
||||||
&auth_ephemeral_kp.seckey,
|
&auth_ephemeral_kp.seckey,
|
||||||
descriptor_cookie, desc_client);
|
descriptor_cookie, desc_client);
|
||||||
smartlist_add(desc->superencrypted_data.clients, desc_client);
|
smartlist_add(desc->superencrypted_data.clients, desc_client);
|
||||||
|
@ -8,9 +8,11 @@
|
|||||||
#include "feature/hs/hs_descriptor.h"
|
#include "feature/hs/hs_descriptor.h"
|
||||||
|
|
||||||
/* Set of functions to help build and test descriptors. */
|
/* Set of functions to help build and test descriptors. */
|
||||||
hs_desc_intro_point_t *hs_helper_build_intro_point(
|
hs_desc_intro_point_t *
|
||||||
const ed25519_keypair_t *signing_kp, time_t now,
|
hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
|
||||||
const char *addr, int legacy);
|
const char *addr, int legacy,
|
||||||
|
const ed25519_keypair_t *intro_auth_kp,
|
||||||
|
const curve25519_keypair_t *intro_enc_kp);
|
||||||
hs_descriptor_t *hs_helper_build_hs_desc_no_ip(
|
hs_descriptor_t *hs_helper_build_hs_desc_no_ip(
|
||||||
const ed25519_keypair_t *signing_kp);
|
const ed25519_keypair_t *signing_kp);
|
||||||
hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
|
hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
|
||||||
@ -21,12 +23,11 @@ hs_descriptor_t *hs_helper_build_hs_desc_with_client_auth(
|
|||||||
const ed25519_keypair_t *signing_kp);
|
const ed25519_keypair_t *signing_kp);
|
||||||
void hs_helper_desc_equal(const hs_descriptor_t *desc1,
|
void hs_helper_desc_equal(const hs_descriptor_t *desc1,
|
||||||
const hs_descriptor_t *desc2);
|
const hs_descriptor_t *desc2);
|
||||||
void
|
struct hs_subcredential_t;
|
||||||
hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
|
void hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
|
||||||
uint8_t *subcred_out);
|
struct hs_subcredential_t *subcred_out);
|
||||||
|
|
||||||
void hs_helper_add_client_auth(const ed25519_public_key_t *service_pk,
|
void hs_helper_add_client_auth(const ed25519_public_key_t *service_pk,
|
||||||
const curve25519_secret_key_t *client_sk);
|
const curve25519_secret_key_t *client_sk);
|
||||||
|
|
||||||
#endif /* !defined(TOR_HS_TEST_HELPERS_H) */
|
#endif /* !defined(TOR_HS_TEST_HELPERS_H) */
|
||||||
|
|
||||||
|
@ -179,6 +179,7 @@ src_test_test_SOURCES += \
|
|||||||
src/test/test_hs_client.c \
|
src/test/test_hs_client.c \
|
||||||
src/test/test_hs_intropoint.c \
|
src/test/test_hs_intropoint.c \
|
||||||
src/test/test_hs_control.c \
|
src/test/test_hs_control.c \
|
||||||
|
src/test/test_hs_ob.c \
|
||||||
src/test/test_handles.c \
|
src/test/test_handles.c \
|
||||||
src/test/test_hs_cache.c \
|
src/test/test_hs_cache.c \
|
||||||
src/test/test_hs_descriptor.c \
|
src/test/test_hs_descriptor.c \
|
||||||
|
@ -721,6 +721,7 @@ struct testgroup_t testgroups[] = {
|
|||||||
{ "hs_dos/", hs_dos_tests },
|
{ "hs_dos/", hs_dos_tests },
|
||||||
{ "hs_intropoint/", hs_intropoint_tests },
|
{ "hs_intropoint/", hs_intropoint_tests },
|
||||||
{ "hs_ntor/", hs_ntor_tests },
|
{ "hs_ntor/", hs_ntor_tests },
|
||||||
|
{ "hs_ob/", hs_ob_tests },
|
||||||
{ "hs_service/", hs_service_tests },
|
{ "hs_service/", hs_service_tests },
|
||||||
{ "introduce/", introduce_tests },
|
{ "introduce/", introduce_tests },
|
||||||
{ "keypin/", keypin_tests },
|
{ "keypin/", keypin_tests },
|
||||||
|
@ -141,6 +141,7 @@ extern struct testcase_t hs_descriptor[];
|
|||||||
extern struct testcase_t hs_dos_tests[];
|
extern struct testcase_t hs_dos_tests[];
|
||||||
extern struct testcase_t hs_intropoint_tests[];
|
extern struct testcase_t hs_intropoint_tests[];
|
||||||
extern struct testcase_t hs_ntor_tests[];
|
extern struct testcase_t hs_ntor_tests[];
|
||||||
|
extern struct testcase_t hs_ob_tests[];
|
||||||
extern struct testcase_t hs_service_tests[];
|
extern struct testcase_t hs_service_tests[];
|
||||||
extern struct testcase_t hs_tests[];
|
extern struct testcase_t hs_tests[];
|
||||||
extern struct testcase_t introduce_tests[];
|
extern struct testcase_t introduce_tests[];
|
||||||
|
@ -370,7 +370,7 @@ test_hsdir_revision_counter_check(void *arg)
|
|||||||
hs_descriptor_t *published_desc = NULL;
|
hs_descriptor_t *published_desc = NULL;
|
||||||
char *published_desc_str = NULL;
|
char *published_desc_str = NULL;
|
||||||
|
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
char *received_desc_str = NULL;
|
char *received_desc_str = NULL;
|
||||||
hs_descriptor_t *received_desc = NULL;
|
hs_descriptor_t *received_desc = NULL;
|
||||||
|
|
||||||
@ -407,11 +407,11 @@ test_hsdir_revision_counter_check(void *arg)
|
|||||||
const ed25519_public_key_t *blinded_key;
|
const ed25519_public_key_t *blinded_key;
|
||||||
|
|
||||||
blinded_key = &published_desc->plaintext_data.blinded_pubkey;
|
blinded_key = &published_desc->plaintext_data.blinded_pubkey;
|
||||||
hs_get_subcredential(&signing_kp.pubkey, blinded_key, subcredential);
|
hs_get_subcredential(&signing_kp.pubkey, blinded_key, &subcredential);
|
||||||
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
|
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
|
||||||
|
|
||||||
retval = hs_desc_decode_descriptor(received_desc_str,
|
retval = hs_desc_decode_descriptor(received_desc_str,
|
||||||
subcredential, NULL, &received_desc);
|
&subcredential, NULL, &received_desc);
|
||||||
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
|
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
|
||||||
tt_assert(received_desc);
|
tt_assert(received_desc);
|
||||||
|
|
||||||
@ -444,7 +444,7 @@ test_hsdir_revision_counter_check(void *arg)
|
|||||||
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
|
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
|
||||||
|
|
||||||
retval = hs_desc_decode_descriptor(received_desc_str,
|
retval = hs_desc_decode_descriptor(received_desc_str,
|
||||||
subcredential, NULL, &received_desc);
|
&subcredential, NULL, &received_desc);
|
||||||
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
|
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
|
||||||
tt_assert(received_desc);
|
tt_assert(received_desc);
|
||||||
|
|
||||||
@ -476,7 +476,7 @@ test_client_cache(void *arg)
|
|||||||
ed25519_keypair_t signing_kp;
|
ed25519_keypair_t signing_kp;
|
||||||
hs_descriptor_t *published_desc = NULL;
|
hs_descriptor_t *published_desc = NULL;
|
||||||
char *published_desc_str = NULL;
|
char *published_desc_str = NULL;
|
||||||
uint8_t wanted_subcredential[DIGEST256_LEN];
|
hs_subcredential_t wanted_subcredential;
|
||||||
response_handler_args_t *args = NULL;
|
response_handler_args_t *args = NULL;
|
||||||
dir_connection_t *conn = NULL;
|
dir_connection_t *conn = NULL;
|
||||||
|
|
||||||
@ -505,8 +505,10 @@ test_client_cache(void *arg)
|
|||||||
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
|
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
|
||||||
NULL, &published_desc_str);
|
NULL, &published_desc_str);
|
||||||
tt_int_op(retval, OP_EQ, 0);
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
memcpy(wanted_subcredential, published_desc->subcredential, DIGEST256_LEN);
|
memcpy(&wanted_subcredential, &published_desc->subcredential,
|
||||||
tt_assert(!fast_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
|
sizeof(hs_subcredential_t));
|
||||||
|
tt_assert(!fast_mem_is_zero((char*)wanted_subcredential.subcred,
|
||||||
|
DIGEST256_LEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test handle_response_fetch_hsdesc_v3() */
|
/* Test handle_response_fetch_hsdesc_v3() */
|
||||||
@ -540,8 +542,9 @@ test_client_cache(void *arg)
|
|||||||
const hs_descriptor_t *cached_desc = NULL;
|
const hs_descriptor_t *cached_desc = NULL;
|
||||||
cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
|
cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
|
||||||
tt_assert(cached_desc);
|
tt_assert(cached_desc);
|
||||||
tt_mem_op(cached_desc->subcredential, OP_EQ, wanted_subcredential,
|
tt_mem_op(cached_desc->subcredential.subcred,
|
||||||
DIGEST256_LEN);
|
OP_EQ, wanted_subcredential.subcred,
|
||||||
|
SUBCRED_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Progress time to next TP and check that desc was cleaned */
|
/* Progress time to next TP and check that desc was cleaned */
|
||||||
|
@ -433,9 +433,10 @@ test_client_pick_intro(void *arg)
|
|||||||
const hs_descriptor_t *fetched_desc =
|
const hs_descriptor_t *fetched_desc =
|
||||||
hs_cache_lookup_as_client(&service_kp.pubkey);
|
hs_cache_lookup_as_client(&service_kp.pubkey);
|
||||||
tt_assert(fetched_desc);
|
tt_assert(fetched_desc);
|
||||||
tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential,
|
tt_mem_op(fetched_desc->subcredential.subcred,
|
||||||
DIGEST256_LEN);
|
OP_EQ, desc->subcredential.subcred,
|
||||||
tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential,
|
SUBCRED_LEN);
|
||||||
|
tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential.subcred,
|
||||||
DIGEST256_LEN));
|
DIGEST256_LEN));
|
||||||
tor_free(encoded);
|
tor_free(encoded);
|
||||||
}
|
}
|
||||||
|
@ -53,14 +53,14 @@ test_validate_address(void *arg)
|
|||||||
setup_full_capture_of_logs(LOG_WARN);
|
setup_full_capture_of_logs(LOG_WARN);
|
||||||
ret = hs_address_is_valid("blah");
|
ret = hs_address_is_valid("blah");
|
||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
expect_log_msg_containing("has an invalid length");
|
expect_log_msg_containing("Invalid length");
|
||||||
teardown_capture_of_logs();
|
teardown_capture_of_logs();
|
||||||
|
|
||||||
setup_full_capture_of_logs(LOG_WARN);
|
setup_full_capture_of_logs(LOG_WARN);
|
||||||
ret = hs_address_is_valid(
|
ret = hs_address_is_valid(
|
||||||
"p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb");
|
"p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb");
|
||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
expect_log_msg_containing("has an invalid length");
|
expect_log_msg_containing("Invalid length");
|
||||||
teardown_capture_of_logs();
|
teardown_capture_of_logs();
|
||||||
|
|
||||||
/* Invalid checksum (taken from prop224) */
|
/* Invalid checksum (taken from prop224) */
|
||||||
@ -83,7 +83,7 @@ test_validate_address(void *arg)
|
|||||||
ret = hs_address_is_valid(
|
ret = hs_address_is_valid(
|
||||||
"????????????????????????????????????????????????????????");
|
"????????????????????????????????????????????????????????");
|
||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
expect_log_msg_containing("can't be decoded");
|
expect_log_msg_containing("Unable to base32 decode");
|
||||||
teardown_capture_of_logs();
|
teardown_capture_of_logs();
|
||||||
|
|
||||||
/* Valid address. */
|
/* Valid address. */
|
||||||
|
@ -221,7 +221,7 @@ test_decode_descriptor(void *arg)
|
|||||||
hs_descriptor_t *desc = NULL;
|
hs_descriptor_t *desc = NULL;
|
||||||
hs_descriptor_t *decoded = NULL;
|
hs_descriptor_t *decoded = NULL;
|
||||||
hs_descriptor_t *desc_no_ip = NULL;
|
hs_descriptor_t *desc_no_ip = NULL;
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
|
|
||||||
(void) arg;
|
(void) arg;
|
||||||
|
|
||||||
@ -230,10 +230,10 @@ test_decode_descriptor(void *arg)
|
|||||||
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
|
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
|
||||||
|
|
||||||
hs_helper_get_subcred_from_identity_keypair(&signing_kp,
|
hs_helper_get_subcred_from_identity_keypair(&signing_kp,
|
||||||
subcredential);
|
&subcredential);
|
||||||
|
|
||||||
/* Give some bad stuff to the decoding function. */
|
/* Give some bad stuff to the decoding function. */
|
||||||
ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential,
|
ret = hs_desc_decode_descriptor("hladfjlkjadf", &subcredential,
|
||||||
NULL, &decoded);
|
NULL, &decoded);
|
||||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
|
||||||
|
|
||||||
@ -241,7 +241,7 @@ test_decode_descriptor(void *arg)
|
|||||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||||
tt_assert(encoded);
|
tt_assert(encoded);
|
||||||
|
|
||||||
ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
|
ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded);
|
||||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||||
tt_assert(decoded);
|
tt_assert(decoded);
|
||||||
|
|
||||||
@ -253,7 +253,7 @@ test_decode_descriptor(void *arg)
|
|||||||
ret = ed25519_keypair_generate(&signing_kp_no_ip, 0);
|
ret = ed25519_keypair_generate(&signing_kp_no_ip, 0);
|
||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
hs_helper_get_subcred_from_identity_keypair(&signing_kp_no_ip,
|
hs_helper_get_subcred_from_identity_keypair(&signing_kp_no_ip,
|
||||||
subcredential);
|
&subcredential);
|
||||||
desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip);
|
desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip);
|
||||||
tt_assert(desc_no_ip);
|
tt_assert(desc_no_ip);
|
||||||
tor_free(encoded);
|
tor_free(encoded);
|
||||||
@ -262,7 +262,7 @@ test_decode_descriptor(void *arg)
|
|||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
tt_assert(encoded);
|
tt_assert(encoded);
|
||||||
hs_descriptor_free(decoded);
|
hs_descriptor_free(decoded);
|
||||||
ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
|
ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded);
|
||||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||||
tt_assert(decoded);
|
tt_assert(decoded);
|
||||||
}
|
}
|
||||||
@ -286,14 +286,14 @@ test_decode_descriptor(void *arg)
|
|||||||
&auth_ephemeral_kp.pubkey, CURVE25519_PUBKEY_LEN);
|
&auth_ephemeral_kp.pubkey, CURVE25519_PUBKEY_LEN);
|
||||||
|
|
||||||
hs_helper_get_subcred_from_identity_keypair(&signing_kp,
|
hs_helper_get_subcred_from_identity_keypair(&signing_kp,
|
||||||
subcredential);
|
&subcredential);
|
||||||
|
|
||||||
/* Build and add the auth client to the descriptor. */
|
/* Build and add the auth client to the descriptor. */
|
||||||
clients = desc->superencrypted_data.clients;
|
clients = desc->superencrypted_data.clients;
|
||||||
if (!clients) {
|
if (!clients) {
|
||||||
clients = smartlist_new();
|
clients = smartlist_new();
|
||||||
}
|
}
|
||||||
hs_desc_build_authorized_client(subcredential,
|
hs_desc_build_authorized_client(&subcredential,
|
||||||
&client_kp.pubkey,
|
&client_kp.pubkey,
|
||||||
&auth_ephemeral_kp.seckey,
|
&auth_ephemeral_kp.seckey,
|
||||||
descriptor_cookie, client);
|
descriptor_cookie, client);
|
||||||
@ -315,21 +315,21 @@ test_decode_descriptor(void *arg)
|
|||||||
|
|
||||||
/* If we do not have the client secret key, the decoding must fail. */
|
/* If we do not have the client secret key, the decoding must fail. */
|
||||||
hs_descriptor_free(decoded);
|
hs_descriptor_free(decoded);
|
||||||
ret = hs_desc_decode_descriptor(encoded, subcredential,
|
ret = hs_desc_decode_descriptor(encoded, &subcredential,
|
||||||
NULL, &decoded);
|
NULL, &decoded);
|
||||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
|
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
|
||||||
tt_assert(!decoded);
|
tt_assert(!decoded);
|
||||||
|
|
||||||
/* If we have an invalid client secret key, the decoding must fail. */
|
/* If we have an invalid client secret key, the decoding must fail. */
|
||||||
hs_descriptor_free(decoded);
|
hs_descriptor_free(decoded);
|
||||||
ret = hs_desc_decode_descriptor(encoded, subcredential,
|
ret = hs_desc_decode_descriptor(encoded, &subcredential,
|
||||||
&invalid_client_kp.seckey, &decoded);
|
&invalid_client_kp.seckey, &decoded);
|
||||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_BAD_CLIENT_AUTH);
|
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_BAD_CLIENT_AUTH);
|
||||||
tt_assert(!decoded);
|
tt_assert(!decoded);
|
||||||
|
|
||||||
/* If we have the client secret key, the decoding must succeed and the
|
/* If we have the client secret key, the decoding must succeed and the
|
||||||
* decoded descriptor must be correct. */
|
* decoded descriptor must be correct. */
|
||||||
ret = hs_desc_decode_descriptor(encoded, subcredential,
|
ret = hs_desc_decode_descriptor(encoded, &subcredential,
|
||||||
&client_kp.seckey, &decoded);
|
&client_kp.seckey, &decoded);
|
||||||
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
|
||||||
tt_assert(decoded);
|
tt_assert(decoded);
|
||||||
@ -762,7 +762,7 @@ test_build_authorized_client(void *arg)
|
|||||||
"07d087f1d8c68393721f6e70316d3b29";
|
"07d087f1d8c68393721f6e70316d3b29";
|
||||||
const char client_pubkey_b16[] =
|
const char client_pubkey_b16[] =
|
||||||
"8c1298fa6050e372f8598f6deca32e27b0ad457741422c2629ebb132cf7fae37";
|
"8c1298fa6050e372f8598f6deca32e27b0ad457741422c2629ebb132cf7fae37";
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
char *mem_op_hex_tmp=NULL;
|
char *mem_op_hex_tmp=NULL;
|
||||||
|
|
||||||
(void) arg;
|
(void) arg;
|
||||||
@ -774,7 +774,7 @@ test_build_authorized_client(void *arg)
|
|||||||
tt_int_op(ret, OP_EQ, 0);
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
curve25519_public_key_generate(&client_auth_pk, &client_auth_sk);
|
curve25519_public_key_generate(&client_auth_pk, &client_auth_sk);
|
||||||
|
|
||||||
memset(subcredential, 42, sizeof(subcredential));
|
memset(subcredential.subcred, 42, sizeof(subcredential));
|
||||||
|
|
||||||
desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
|
desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
|
||||||
|
|
||||||
@ -795,7 +795,7 @@ test_build_authorized_client(void *arg)
|
|||||||
|
|
||||||
testing_enable_prefilled_rng("\x01", 1);
|
testing_enable_prefilled_rng("\x01", 1);
|
||||||
|
|
||||||
hs_desc_build_authorized_client(subcredential,
|
hs_desc_build_authorized_client(&subcredential,
|
||||||
&client_auth_pk, &auth_ephemeral_sk,
|
&client_auth_pk, &auth_ephemeral_sk,
|
||||||
descriptor_cookie, desc_client);
|
descriptor_cookie, desc_client);
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ test_hs_ntor(void *arg)
|
|||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
|
|
||||||
ed25519_keypair_t service_intro_auth_keypair;
|
ed25519_keypair_t service_intro_auth_keypair;
|
||||||
curve25519_keypair_t service_intro_enc_keypair;
|
curve25519_keypair_t service_intro_enc_keypair;
|
||||||
@ -42,7 +42,7 @@ test_hs_ntor(void *arg)
|
|||||||
/* Generate fake data for this unittest */
|
/* Generate fake data for this unittest */
|
||||||
{
|
{
|
||||||
/* Generate fake subcredential */
|
/* Generate fake subcredential */
|
||||||
memset(subcredential, 'Z', DIGEST256_LEN);
|
memset(subcredential.subcred, 'Z', DIGEST256_LEN);
|
||||||
|
|
||||||
/* service */
|
/* service */
|
||||||
curve25519_keypair_generate(&service_intro_enc_keypair, 0);
|
curve25519_keypair_generate(&service_intro_enc_keypair, 0);
|
||||||
@ -57,7 +57,7 @@ test_hs_ntor(void *arg)
|
|||||||
hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
|
hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
|
||||||
&service_intro_enc_keypair.pubkey,
|
&service_intro_enc_keypair.pubkey,
|
||||||
&client_ephemeral_enc_keypair,
|
&client_ephemeral_enc_keypair,
|
||||||
subcredential,
|
&subcredential,
|
||||||
&client_hs_ntor_intro_cell_keys);
|
&client_hs_ntor_intro_cell_keys);
|
||||||
tt_int_op(retval, OP_EQ, 0);
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ test_hs_ntor(void *arg)
|
|||||||
hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
|
hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
|
||||||
&service_intro_enc_keypair,
|
&service_intro_enc_keypair,
|
||||||
&client_ephemeral_enc_keypair.pubkey,
|
&client_ephemeral_enc_keypair.pubkey,
|
||||||
subcredential,
|
&subcredential,
|
||||||
&service_hs_ntor_intro_cell_keys);
|
&service_hs_ntor_intro_cell_keys);
|
||||||
tt_int_op(retval, OP_EQ, 0);
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ client1(int argc, char **argv)
|
|||||||
curve25519_public_key_t intro_enc_pubkey;
|
curve25519_public_key_t intro_enc_pubkey;
|
||||||
ed25519_public_key_t intro_auth_pubkey;
|
ed25519_public_key_t intro_auth_pubkey;
|
||||||
curve25519_keypair_t client_ephemeral_enc_keypair;
|
curve25519_keypair_t client_ephemeral_enc_keypair;
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
|
|
||||||
/* Output */
|
/* Output */
|
||||||
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
||||||
@ -65,7 +65,7 @@ client1(int argc, char **argv)
|
|||||||
BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key,
|
BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key,
|
||||||
CURVE25519_SECKEY_LEN);
|
CURVE25519_SECKEY_LEN);
|
||||||
BASE16(5, subcredential, DIGEST256_LEN);
|
BASE16(5, subcredential.subcred, DIGEST256_LEN);
|
||||||
|
|
||||||
/* Generate keypair */
|
/* Generate keypair */
|
||||||
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
||||||
@ -74,7 +74,7 @@ client1(int argc, char **argv)
|
|||||||
retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey,
|
retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey,
|
||||||
&intro_enc_pubkey,
|
&intro_enc_pubkey,
|
||||||
&client_ephemeral_enc_keypair,
|
&client_ephemeral_enc_keypair,
|
||||||
subcredential,
|
&subcredential,
|
||||||
&hs_ntor_intro_cell_keys);
|
&hs_ntor_intro_cell_keys);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
goto done;
|
goto done;
|
||||||
@ -106,7 +106,7 @@ server1(int argc, char **argv)
|
|||||||
curve25519_keypair_t intro_enc_keypair;
|
curve25519_keypair_t intro_enc_keypair;
|
||||||
ed25519_public_key_t intro_auth_pubkey;
|
ed25519_public_key_t intro_auth_pubkey;
|
||||||
curve25519_public_key_t client_ephemeral_enc_pubkey;
|
curve25519_public_key_t client_ephemeral_enc_pubkey;
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
|
|
||||||
/* Output */
|
/* Output */
|
||||||
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
||||||
@ -119,7 +119,7 @@ server1(int argc, char **argv)
|
|||||||
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
||||||
BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN);
|
BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN);
|
||||||
BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
BASE16(5, subcredential, DIGEST256_LEN);
|
BASE16(5, subcredential.subcred, DIGEST256_LEN);
|
||||||
|
|
||||||
/* Generate keypair */
|
/* Generate keypair */
|
||||||
curve25519_public_key_generate(&intro_enc_keypair.pubkey,
|
curve25519_public_key_generate(&intro_enc_keypair.pubkey,
|
||||||
@ -130,7 +130,7 @@ server1(int argc, char **argv)
|
|||||||
retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey,
|
retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey,
|
||||||
&intro_enc_keypair,
|
&intro_enc_keypair,
|
||||||
&client_ephemeral_enc_pubkey,
|
&client_ephemeral_enc_pubkey,
|
||||||
subcredential,
|
&subcredential,
|
||||||
&hs_ntor_intro_cell_keys);
|
&hs_ntor_intro_cell_keys);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
goto done;
|
goto done;
|
||||||
@ -188,7 +188,7 @@ client2(int argc, char **argv)
|
|||||||
ed25519_public_key_t intro_auth_pubkey;
|
ed25519_public_key_t intro_auth_pubkey;
|
||||||
curve25519_keypair_t client_ephemeral_enc_keypair;
|
curve25519_keypair_t client_ephemeral_enc_keypair;
|
||||||
curve25519_public_key_t service_ephemeral_rend_pubkey;
|
curve25519_public_key_t service_ephemeral_rend_pubkey;
|
||||||
uint8_t subcredential[DIGEST256_LEN];
|
hs_subcredential_t subcredential;
|
||||||
|
|
||||||
/* Output */
|
/* Output */
|
||||||
hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
|
hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
|
||||||
@ -201,7 +201,7 @@ client2(int argc, char **argv)
|
|||||||
CURVE25519_SECKEY_LEN);
|
CURVE25519_SECKEY_LEN);
|
||||||
BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
BASE16(6, subcredential, DIGEST256_LEN);
|
BASE16(6, subcredential.subcred, DIGEST256_LEN);
|
||||||
|
|
||||||
/* Generate keypair */
|
/* Generate keypair */
|
||||||
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
||||||
|
268
src/test/test_hs_ob.c
Normal file
268
src/test/test_hs_ob.c
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/* Copyright (c) 2020, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file test_hs_ob.c
|
||||||
|
* \brief Test hidden service onion balance functionality.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CONFIG_PRIVATE
|
||||||
|
#define HS_SERVICE_PRIVATE
|
||||||
|
#define HS_OB_PRIVATE
|
||||||
|
|
||||||
|
#include "test/test.h"
|
||||||
|
#include "test/test_helpers.h"
|
||||||
|
#include "test/log_test_helpers.h"
|
||||||
|
|
||||||
|
#include "app/config/config.h"
|
||||||
|
#include "feature/hs/hs_config.h"
|
||||||
|
#include "feature/hs/hs_ob.h"
|
||||||
|
#include "feature/hs/hs_service.h"
|
||||||
|
#include "feature/nodelist/networkstatus.h"
|
||||||
|
#include "feature/nodelist/networkstatus_st.h"
|
||||||
|
|
||||||
|
static ed25519_keypair_t onion_addr_kp_1;
|
||||||
|
static char onion_addr_1[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||||
|
|
||||||
|
static ed25519_keypair_t onion_addr_kp_2;
|
||||||
|
static char onion_addr_2[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||||
|
|
||||||
|
static bool config_is_good = true;
|
||||||
|
|
||||||
|
static int
|
||||||
|
helper_tor_config(const char *conf)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
or_options_t *options = helper_parse_options(conf);
|
||||||
|
tt_assert(options);
|
||||||
|
ret = hs_config_service_all(options, 0);
|
||||||
|
done:
|
||||||
|
or_options_free(options);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static networkstatus_t mock_ns;
|
||||||
|
|
||||||
|
static networkstatus_t *
|
||||||
|
mock_networkstatus_get_live_consensus(time_t now)
|
||||||
|
{
|
||||||
|
(void) now;
|
||||||
|
return &mock_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
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("hs3" PATH_SEPARATOR "ob_config"))) {
|
||||||
|
if (config_is_good) {
|
||||||
|
tor_asprintf(&ret, "MasterOnionAddress %s.onion\n"
|
||||||
|
"MasterOnionAddress %s.onion\n",
|
||||||
|
onion_addr_1, onion_addr_2);
|
||||||
|
} else {
|
||||||
|
tor_asprintf(&ret, "MasterOnionAddress JUNKJUNKJUNK.onion\n"
|
||||||
|
"UnknownOption BLAH\n");
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_parse_config_file(void *arg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *conf = NULL;
|
||||||
|
const ed25519_public_key_t *pkey;
|
||||||
|
|
||||||
|
(void) arg;
|
||||||
|
|
||||||
|
hs_init();
|
||||||
|
|
||||||
|
MOCK(read_file_to_str, mock_read_file_to_str);
|
||||||
|
|
||||||
|
#define fmt_conf \
|
||||||
|
"HiddenServiceDir %s\n" \
|
||||||
|
"HiddenServicePort 22\n" \
|
||||||
|
"HiddenServiceOnionBalanceInstance 1\n"
|
||||||
|
tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
|
||||||
|
#undef fmt_conf
|
||||||
|
|
||||||
|
/* Build the OB frontend onion addresses. */
|
||||||
|
ed25519_keypair_generate(&onion_addr_kp_1, 0);
|
||||||
|
hs_build_address(&onion_addr_kp_1.pubkey, HS_VERSION_THREE, onion_addr_1);
|
||||||
|
ed25519_keypair_generate(&onion_addr_kp_2, 0);
|
||||||
|
hs_build_address(&onion_addr_kp_2.pubkey, HS_VERSION_THREE, onion_addr_2);
|
||||||
|
|
||||||
|
ret = helper_tor_config(conf);
|
||||||
|
tor_free(conf);
|
||||||
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
|
||||||
|
/* Load the keys for the service. After that, the v3 service should be
|
||||||
|
* registered in the global map and we'll be able to access it. */
|
||||||
|
tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
|
||||||
|
hs_service_load_all_keys();
|
||||||
|
tt_int_op(get_hs_service_map_size(), OP_EQ, 1);
|
||||||
|
const hs_service_t *s = get_first_service();
|
||||||
|
tt_assert(s);
|
||||||
|
tt_assert(s->config.ob_master_pubkeys);
|
||||||
|
tt_assert(hs_ob_service_is_instance(s));
|
||||||
|
tt_assert(smartlist_len(s->config.ob_master_pubkeys) == 2);
|
||||||
|
|
||||||
|
/* Test the public keys we've added. */
|
||||||
|
pkey = smartlist_get(s->config.ob_master_pubkeys, 0);
|
||||||
|
tt_mem_op(&onion_addr_kp_1.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
|
||||||
|
pkey = smartlist_get(s->config.ob_master_pubkeys, 1);
|
||||||
|
tt_mem_op(&onion_addr_kp_2.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
|
||||||
|
|
||||||
|
done:
|
||||||
|
hs_free_all();
|
||||||
|
|
||||||
|
UNMOCK(read_file_to_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_parse_config_file_bad(void *arg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *conf = NULL;
|
||||||
|
|
||||||
|
(void) arg;
|
||||||
|
|
||||||
|
hs_init();
|
||||||
|
|
||||||
|
MOCK(read_file_to_str, mock_read_file_to_str);
|
||||||
|
|
||||||
|
/* Indicate mock_read_file_to_str() to use the bad config. */
|
||||||
|
config_is_good = false;
|
||||||
|
|
||||||
|
#define fmt_conf \
|
||||||
|
"HiddenServiceDir %s\n" \
|
||||||
|
"HiddenServicePort 22\n" \
|
||||||
|
"HiddenServiceOnionBalanceInstance 1\n"
|
||||||
|
tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
|
||||||
|
#undef fmt_conf
|
||||||
|
|
||||||
|
setup_full_capture_of_logs(LOG_INFO);
|
||||||
|
ret = helper_tor_config(conf);
|
||||||
|
tor_free(conf);
|
||||||
|
tt_int_op(ret, OP_EQ, -1);
|
||||||
|
expect_log_msg_containing("OnionBalance: MasterOnionAddress "
|
||||||
|
"JUNKJUNKJUNK.onion is invalid");
|
||||||
|
expect_log_msg_containing("Found unrecognized option \'UnknownOption\'; "
|
||||||
|
"saving it.");
|
||||||
|
teardown_capture_of_logs();
|
||||||
|
|
||||||
|
done:
|
||||||
|
hs_free_all();
|
||||||
|
|
||||||
|
UNMOCK(read_file_to_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_get_subcredentials(void *arg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
hs_service_t *service = NULL;
|
||||||
|
hs_service_config_t config;
|
||||||
|
hs_subcredential_t *subcreds = NULL;
|
||||||
|
|
||||||
|
(void) arg;
|
||||||
|
|
||||||
|
MOCK(networkstatus_get_live_consensus,
|
||||||
|
mock_networkstatus_get_live_consensus);
|
||||||
|
|
||||||
|
/* Setup consensus with proper time so we can compute the time period. */
|
||||||
|
ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
|
||||||
|
&mock_ns.valid_after);
|
||||||
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
|
||||||
|
&mock_ns.fresh_until);
|
||||||
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
|
||||||
|
config.ob_master_pubkeys = smartlist_new();
|
||||||
|
tt_assert(config.ob_master_pubkeys);
|
||||||
|
|
||||||
|
/* Set up an instance */
|
||||||
|
service = tor_malloc_zero(sizeof(hs_service_t));
|
||||||
|
service->config = config;
|
||||||
|
/* Setup the service descriptors */
|
||||||
|
service->desc_current = service_descriptor_new();
|
||||||
|
service->desc_next = service_descriptor_new();
|
||||||
|
|
||||||
|
/* First try to compute subcredentials but with no OB keys. Make sure that
|
||||||
|
* subcreds get NULLed. To do this check we first poison subcreds. */
|
||||||
|
subcreds = (void*)999;
|
||||||
|
tt_ptr_op(subcreds, OP_NE, NULL);
|
||||||
|
size_t num = compute_subcredentials(service, &subcreds);
|
||||||
|
tt_ptr_op(subcreds, OP_EQ, NULL);
|
||||||
|
|
||||||
|
/* Generate a keypair to add to the OB keys list. */
|
||||||
|
ed25519_keypair_generate(&onion_addr_kp_1, 0);
|
||||||
|
smartlist_add(config.ob_master_pubkeys, &onion_addr_kp_1.pubkey);
|
||||||
|
|
||||||
|
/* Set up the instance subcredentials */
|
||||||
|
char current_subcred[SUBCRED_LEN];
|
||||||
|
char next_subcred[SUBCRED_LEN];
|
||||||
|
memset(current_subcred, 'C', SUBCRED_LEN);
|
||||||
|
memset(next_subcred, 'N', SUBCRED_LEN);
|
||||||
|
memcpy(service->desc_current->desc->subcredential.subcred, current_subcred,
|
||||||
|
SUBCRED_LEN);
|
||||||
|
memcpy(service->desc_next->desc->subcredential.subcred, next_subcred,
|
||||||
|
SUBCRED_LEN);
|
||||||
|
|
||||||
|
/* See that subcreds are computed properly */
|
||||||
|
num = compute_subcredentials(service, &subcreds);
|
||||||
|
/* 5 subcredentials: 3 for the frontend, 2 for the instance */
|
||||||
|
tt_uint_op(num, OP_EQ, 5);
|
||||||
|
tt_ptr_op(subcreds, OP_NE, NULL);
|
||||||
|
|
||||||
|
/* Validate the subcredentials we just got. We'll build them oursevles with
|
||||||
|
* the right time period steps and compare. */
|
||||||
|
const uint64_t tp = hs_get_time_period_num(0);
|
||||||
|
const int steps[3] = {0, -1, 1};
|
||||||
|
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
hs_subcredential_t subcredential;
|
||||||
|
ed25519_public_key_t blinded_pubkey;
|
||||||
|
hs_build_blinded_pubkey(&onion_addr_kp_1.pubkey, NULL, 0, tp + steps[i],
|
||||||
|
&blinded_pubkey);
|
||||||
|
hs_get_subcredential(&onion_addr_kp_1.pubkey, &blinded_pubkey,
|
||||||
|
&subcredential);
|
||||||
|
tt_mem_op(subcreds[i].subcred, OP_EQ, subcredential.subcred,
|
||||||
|
SUBCRED_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
tt_mem_op(subcreds[i++].subcred, OP_EQ, current_subcred, SUBCRED_LEN);
|
||||||
|
tt_mem_op(subcreds[i++].subcred, OP_EQ, next_subcred, SUBCRED_LEN);
|
||||||
|
|
||||||
|
done:
|
||||||
|
tor_free(subcreds);
|
||||||
|
|
||||||
|
smartlist_free(config.ob_master_pubkeys);
|
||||||
|
if (service) {
|
||||||
|
memset(&service->config, 0, sizeof(hs_service_config_t));
|
||||||
|
hs_service_free(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMOCK(networkstatus_get_live_consensus);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct testcase_t hs_ob_tests[] = {
|
||||||
|
{ "parse_config_file", test_parse_config_file, TT_FORK,
|
||||||
|
NULL, NULL },
|
||||||
|
{ "parse_config_file_bad", test_parse_config_file_bad, TT_FORK,
|
||||||
|
NULL, NULL },
|
||||||
|
|
||||||
|
{ "get_subcredentials", test_get_subcredentials, TT_FORK,
|
||||||
|
NULL, NULL },
|
||||||
|
|
||||||
|
END_OF_TESTCASES
|
||||||
|
};
|
@ -51,6 +51,8 @@
|
|||||||
#include "feature/hs/hs_common.h"
|
#include "feature/hs/hs_common.h"
|
||||||
#include "feature/hs/hs_config.h"
|
#include "feature/hs/hs_config.h"
|
||||||
#include "feature/hs/hs_ident.h"
|
#include "feature/hs/hs_ident.h"
|
||||||
|
#include "feature/hs/hs_ob.h"
|
||||||
|
#include "feature/hs/hs_cell.h"
|
||||||
#include "feature/hs/hs_intropoint.h"
|
#include "feature/hs/hs_intropoint.h"
|
||||||
#include "feature/hs/hs_service.h"
|
#include "feature/hs/hs_service.h"
|
||||||
#include "feature/nodelist/networkstatus.h"
|
#include "feature/nodelist/networkstatus.h"
|
||||||
@ -109,6 +111,9 @@ mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t relay_payload_len;
|
||||||
|
static char relay_payload[RELAY_PAYLOAD_SIZE];
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
|
mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
|
||||||
uint8_t relay_command, const char *payload,
|
uint8_t relay_command, const char *payload,
|
||||||
@ -124,6 +129,10 @@ mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
|
|||||||
(void) cpath_layer;
|
(void) cpath_layer;
|
||||||
(void) filename;
|
(void) filename;
|
||||||
(void) lineno;
|
(void) lineno;
|
||||||
|
|
||||||
|
memcpy(relay_payload, payload, payload_len);
|
||||||
|
relay_payload_len = payload_len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1160,7 +1169,7 @@ test_closing_intro_circs(void *arg)
|
|||||||
|
|
||||||
/** Test sending and receiving introduce2 cells */
|
/** Test sending and receiving introduce2 cells */
|
||||||
static void
|
static void
|
||||||
test_introduce2(void *arg)
|
test_bad_introduce2(void *arg)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
|
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
|
||||||
@ -2169,6 +2178,348 @@ test_export_client_circuit_id(void *arg)
|
|||||||
tor_free(cp2);
|
tor_free(cp2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static smartlist_t *
|
||||||
|
mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
|
||||||
|
{
|
||||||
|
(void) node;
|
||||||
|
(void) direct_conn;
|
||||||
|
|
||||||
|
smartlist_t *lspecs = smartlist_new();
|
||||||
|
link_specifier_t *ls_legacy = link_specifier_new();
|
||||||
|
smartlist_add(lspecs, ls_legacy);
|
||||||
|
|
||||||
|
return lspecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static node_t *fake_node = NULL;
|
||||||
|
|
||||||
|
static const node_t *
|
||||||
|
mock_build_state_get_exit_node(cpath_build_state_t *state)
|
||||||
|
{
|
||||||
|
(void) state;
|
||||||
|
|
||||||
|
if (!fake_node) {
|
||||||
|
curve25519_secret_key_t seckey;
|
||||||
|
curve25519_secret_key_generate(&seckey, 0);
|
||||||
|
|
||||||
|
fake_node = tor_malloc_zero(sizeof(node_t));
|
||||||
|
fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t));
|
||||||
|
fake_node->ri->onion_curve25519_pkey =
|
||||||
|
tor_malloc_zero(sizeof(curve25519_public_key_t));
|
||||||
|
curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey,
|
||||||
|
&seckey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fake_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mock_launch_rendezvous_point_circuit(const hs_service_t *service,
|
||||||
|
const hs_service_intro_point_t *ip,
|
||||||
|
const hs_cell_introduce2_data_t *data)
|
||||||
|
{
|
||||||
|
(void) service;
|
||||||
|
(void) ip;
|
||||||
|
(void) data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that INTRO2 cells are handled well by onion services in the normal
|
||||||
|
* case and also when onionbalance is enabled.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
test_intro2_handling(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
MOCK(build_state_get_exit_node, mock_build_state_get_exit_node);
|
||||||
|
MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
|
||||||
|
MOCK(node_get_link_specifier_smartlist,
|
||||||
|
mock_node_get_link_specifier_smartlist);
|
||||||
|
MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit);
|
||||||
|
|
||||||
|
memset(relay_payload, 0, sizeof(relay_payload));
|
||||||
|
|
||||||
|
int retval;
|
||||||
|
time_t now = 0101010101;
|
||||||
|
update_approx_time(now);
|
||||||
|
|
||||||
|
/** OK this is the play:
|
||||||
|
*
|
||||||
|
* In Act I, we have a standalone onion service X (without onionbalance
|
||||||
|
* enabled). We test that X can properly handle INTRO2 cells sent by a
|
||||||
|
* client Alice.
|
||||||
|
*
|
||||||
|
* In Act II, we create an onionbalance setup with frontend being Z which
|
||||||
|
* includes instances X and Y. We then setup onionbalance on X and test that
|
||||||
|
* Alice who addresses Z can communicate with X through INTRO2 cells.
|
||||||
|
*
|
||||||
|
* In Act III, we test that Alice can also communicate with X
|
||||||
|
* directly even tho onionbalance is enabled.
|
||||||
|
*
|
||||||
|
* And finally in Act IV, we check various cases where the INTRO2 cell
|
||||||
|
* should not go through because the subcredentials don't line up
|
||||||
|
* (e.g. Alice sends INTRO2 to X using Y's subcredential).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Let's start with some setup! Create the instances and the frontend
|
||||||
|
service, create Alice, etc: */
|
||||||
|
|
||||||
|
/* Create instance X */
|
||||||
|
hs_service_t x_service;
|
||||||
|
memset(&x_service, 0, sizeof(hs_service_t));
|
||||||
|
/* Disable onionbalance */
|
||||||
|
x_service.config.ob_master_pubkeys = NULL;
|
||||||
|
x_service.state.replay_cache_rend_cookie = replaycache_new(0,0);
|
||||||
|
|
||||||
|
/* Create subcredential for x: */
|
||||||
|
ed25519_keypair_t x_identity_keypair;
|
||||||
|
hs_subcredential_t x_subcred;
|
||||||
|
ed25519_keypair_generate(&x_identity_keypair, 0);
|
||||||
|
hs_helper_get_subcred_from_identity_keypair(&x_identity_keypair,
|
||||||
|
&x_subcred);
|
||||||
|
|
||||||
|
/* Create the x instance's intro point */
|
||||||
|
hs_service_intro_point_t *x_ip = NULL;
|
||||||
|
{
|
||||||
|
curve25519_secret_key_t seckey;
|
||||||
|
curve25519_public_key_t pkey;
|
||||||
|
curve25519_secret_key_generate(&seckey, 0);
|
||||||
|
curve25519_public_key_generate(&pkey, &seckey);
|
||||||
|
|
||||||
|
node_t intro_node;
|
||||||
|
memset(&intro_node, 0, sizeof(intro_node));
|
||||||
|
routerinfo_t ri;
|
||||||
|
memset(&ri, 0, sizeof(routerinfo_t));
|
||||||
|
ri.onion_curve25519_pkey = &pkey;
|
||||||
|
intro_node.ri = &ri;
|
||||||
|
|
||||||
|
x_ip = service_intro_point_new(&intro_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create z frontend's subcredential */
|
||||||
|
ed25519_keypair_t z_identity_keypair;
|
||||||
|
hs_subcredential_t z_subcred;
|
||||||
|
ed25519_keypair_generate(&z_identity_keypair, 0);
|
||||||
|
hs_helper_get_subcred_from_identity_keypair(&z_identity_keypair,
|
||||||
|
&z_subcred);
|
||||||
|
|
||||||
|
/* Create y instance's subcredential */
|
||||||
|
ed25519_keypair_t y_identity_keypair;
|
||||||
|
hs_subcredential_t y_subcred;
|
||||||
|
ed25519_keypair_generate(&y_identity_keypair, 0);
|
||||||
|
hs_helper_get_subcred_from_identity_keypair(&y_identity_keypair,
|
||||||
|
&y_subcred);
|
||||||
|
|
||||||
|
/* Create Alice's intro point */
|
||||||
|
hs_desc_intro_point_t *alice_ip;
|
||||||
|
ed25519_keypair_t signing_kp;
|
||||||
|
ed25519_keypair_generate(&signing_kp, 0);
|
||||||
|
alice_ip = hs_helper_build_intro_point(&signing_kp, now, "1.2.3.4", 0,
|
||||||
|
&x_ip->auth_key_kp,
|
||||||
|
&x_ip->enc_key_kp);
|
||||||
|
|
||||||
|
/* Create Alice's intro and rend circuits */
|
||||||
|
origin_circuit_t *intro_circ = origin_circuit_new();
|
||||||
|
intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
|
||||||
|
intro_circ->cpath->prev = intro_circ->cpath;
|
||||||
|
intro_circ->hs_ident = tor_malloc_zero(sizeof(*intro_circ->hs_ident));
|
||||||
|
origin_circuit_t rend_circ;
|
||||||
|
rend_circ.hs_ident = tor_malloc_zero(sizeof(*rend_circ.hs_ident));
|
||||||
|
curve25519_keypair_generate(&rend_circ.hs_ident->rendezvous_client_kp, 0);
|
||||||
|
memset(rend_circ.hs_ident->rendezvous_cookie, 'r', HS_REND_COOKIE_LEN);
|
||||||
|
|
||||||
|
/* ************************************************************ */
|
||||||
|
|
||||||
|
/* Act I:
|
||||||
|
*
|
||||||
|
* Where Alice connects to X without onionbalance in the picture */
|
||||||
|
|
||||||
|
/* Create INTRODUCE1 */
|
||||||
|
tt_assert(fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
|
||||||
|
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
|
||||||
|
alice_ip, &x_subcred);
|
||||||
|
|
||||||
|
/* Check that the payload was written successfully */
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
|
||||||
|
tt_int_op(relay_payload_len, OP_NE, 0);
|
||||||
|
|
||||||
|
/* Handle the cell */
|
||||||
|
retval = hs_circ_handle_introduce2(&x_service,
|
||||||
|
intro_circ, x_ip,
|
||||||
|
&x_subcred,
|
||||||
|
(uint8_t*)relay_payload,relay_payload_len);
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
|
||||||
|
/* ************************************************************ */
|
||||||
|
|
||||||
|
/* Act II:
|
||||||
|
*
|
||||||
|
* We now create an onionbalance setup with Z being the frontend and X and Y
|
||||||
|
* being the backend instances. Make sure that Alice can talk with the
|
||||||
|
* backend instance X even tho she thinks she is talking to the frontend Z.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Now configure the X instance to do onionbalance with Z as the frontend */
|
||||||
|
x_service.config.ob_master_pubkeys = smartlist_new();
|
||||||
|
smartlist_add(x_service.config.ob_master_pubkeys,
|
||||||
|
&z_identity_keypair.pubkey);
|
||||||
|
|
||||||
|
/* Create descriptors for x and load next descriptor with the x's
|
||||||
|
* subcredential so that it can accept connections for itself. */
|
||||||
|
x_service.desc_current = service_descriptor_new();
|
||||||
|
memset(x_service.desc_current->desc->subcredential.subcred, 'C',SUBCRED_LEN);
|
||||||
|
x_service.desc_next = service_descriptor_new();
|
||||||
|
memcpy(&x_service.desc_next->desc->subcredential, &x_subcred, SUBCRED_LEN);
|
||||||
|
|
||||||
|
/* Refresh OB keys */
|
||||||
|
hs_ob_refresh_keys(&x_service);
|
||||||
|
|
||||||
|
/* Create INTRODUCE1 from Alice to X through Z */
|
||||||
|
memset(relay_payload, 0, sizeof(relay_payload));
|
||||||
|
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
|
||||||
|
alice_ip, &z_subcred);
|
||||||
|
|
||||||
|
/* Check that the payload was written successfully */
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
|
||||||
|
tt_int_op(relay_payload_len, OP_NE, 0);
|
||||||
|
|
||||||
|
/* Deliver INTRODUCE1 to X even tho it carries Z's subcredential */
|
||||||
|
replaycache_free(x_service.state.replay_cache_rend_cookie);
|
||||||
|
x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
|
||||||
|
|
||||||
|
retval = hs_circ_handle_introduce2(&x_service,
|
||||||
|
intro_circ, x_ip,
|
||||||
|
&z_subcred,
|
||||||
|
(uint8_t*)relay_payload, relay_payload_len);
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
|
||||||
|
replaycache_free(x_ip->replay_cache);
|
||||||
|
x_ip->replay_cache = replaycache_new(0, 0);
|
||||||
|
|
||||||
|
replaycache_free(x_service.state.replay_cache_rend_cookie);
|
||||||
|
x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
|
||||||
|
|
||||||
|
/* ************************************************************ */
|
||||||
|
|
||||||
|
/* Act III:
|
||||||
|
*
|
||||||
|
* Now send a direct INTRODUCE cell from Alice to X using X's subcredential
|
||||||
|
* and check that it succeeds even with onionbalance enabled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Refresh OB keys (just to check for memleaks) */
|
||||||
|
hs_ob_refresh_keys(&x_service);
|
||||||
|
|
||||||
|
/* Create INTRODUCE1 from Alice to X using X's subcred. */
|
||||||
|
memset(relay_payload, 0, sizeof(relay_payload));
|
||||||
|
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
|
||||||
|
alice_ip, &x_subcred);
|
||||||
|
|
||||||
|
/* Check that the payload was written successfully */
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
|
||||||
|
tt_int_op(relay_payload_len, OP_NE, 0);
|
||||||
|
|
||||||
|
/* Send INTRODUCE1 to X with X's subcredential (should succeed) */
|
||||||
|
replaycache_free(x_service.state.replay_cache_rend_cookie);
|
||||||
|
x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
|
||||||
|
|
||||||
|
retval = hs_circ_handle_introduce2(&x_service,
|
||||||
|
intro_circ, x_ip,
|
||||||
|
&x_subcred,
|
||||||
|
(uint8_t*)relay_payload, relay_payload_len);
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
|
||||||
|
/* ************************************************************ */
|
||||||
|
|
||||||
|
/* Act IV:
|
||||||
|
*
|
||||||
|
* Test cases where the INTRO2 cell should not be able to decode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Try sending the exact same INTRODUCE2 cell again and see that the intro
|
||||||
|
* point replay cache triggers: */
|
||||||
|
setup_full_capture_of_logs(LOG_WARN);
|
||||||
|
retval = hs_circ_handle_introduce2(&x_service,
|
||||||
|
intro_circ, x_ip,
|
||||||
|
&x_subcred,
|
||||||
|
(uint8_t*)relay_payload, relay_payload_len);
|
||||||
|
tt_int_op(retval, OP_EQ, -1);
|
||||||
|
expect_log_msg_containing("with the same ENCRYPTED section");
|
||||||
|
teardown_capture_of_logs();
|
||||||
|
|
||||||
|
/* Now cleanup the intro point replay cache but not the service replay cache
|
||||||
|
and see that this one triggers this time. */
|
||||||
|
replaycache_free(x_ip->replay_cache);
|
||||||
|
x_ip->replay_cache = replaycache_new(0, 0);
|
||||||
|
setup_full_capture_of_logs(LOG_INFO);
|
||||||
|
retval = hs_circ_handle_introduce2(&x_service,
|
||||||
|
intro_circ, x_ip,
|
||||||
|
&x_subcred,
|
||||||
|
(uint8_t*)relay_payload, relay_payload_len);
|
||||||
|
tt_int_op(retval, OP_EQ, -1);
|
||||||
|
expect_log_msg_containing("with same REND_COOKIE");
|
||||||
|
teardown_capture_of_logs();
|
||||||
|
|
||||||
|
/* Now just to make sure cleanup both replay caches and make sure that the
|
||||||
|
cell gets through */
|
||||||
|
replaycache_free(x_ip->replay_cache);
|
||||||
|
x_ip->replay_cache = replaycache_new(0, 0);
|
||||||
|
replaycache_free(x_service.state.replay_cache_rend_cookie);
|
||||||
|
x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
|
||||||
|
retval = hs_circ_handle_introduce2(&x_service,
|
||||||
|
intro_circ, x_ip,
|
||||||
|
&x_subcred,
|
||||||
|
(uint8_t*)relay_payload, relay_payload_len);
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
|
||||||
|
/* As a final thing, create an INTRODUCE1 cell from Alice to X using Y's
|
||||||
|
* subcred (should fail since Y is just another instance and not the frontend
|
||||||
|
* service!) */
|
||||||
|
memset(relay_payload, 0, sizeof(relay_payload));
|
||||||
|
retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
|
||||||
|
alice_ip, &y_subcred);
|
||||||
|
tt_int_op(retval, OP_EQ, 0);
|
||||||
|
|
||||||
|
/* Check that the payload was written successfully */
|
||||||
|
tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
|
||||||
|
tt_int_op(relay_payload_len, OP_NE, 0);
|
||||||
|
|
||||||
|
retval = hs_circ_handle_introduce2(&x_service,
|
||||||
|
intro_circ, x_ip,
|
||||||
|
&y_subcred,
|
||||||
|
(uint8_t*)relay_payload, relay_payload_len);
|
||||||
|
tt_int_op(retval, OP_EQ, -1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
/* Start cleaning up X */
|
||||||
|
replaycache_free(x_service.state.replay_cache_rend_cookie);
|
||||||
|
smartlist_free(x_service.config.ob_master_pubkeys);
|
||||||
|
tor_free(x_service.ob_subcreds);
|
||||||
|
service_descriptor_free(x_service.desc_current);
|
||||||
|
service_descriptor_free(x_service.desc_next);
|
||||||
|
service_intro_point_free(x_ip);
|
||||||
|
|
||||||
|
/* Clean up Alice */
|
||||||
|
hs_desc_intro_point_free(alice_ip);
|
||||||
|
tor_free(rend_circ.hs_ident);
|
||||||
|
|
||||||
|
if (fake_node) {
|
||||||
|
tor_free(fake_node->ri->onion_curve25519_pkey);
|
||||||
|
tor_free(fake_node->ri);
|
||||||
|
tor_free(fake_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMOCK(build_state_get_exit_node);
|
||||||
|
UNMOCK(relay_send_command_from_edge_);
|
||||||
|
UNMOCK(node_get_link_specifier_smartlist);
|
||||||
|
UNMOCK(launch_rendezvous_point_circuit);
|
||||||
|
}
|
||||||
|
|
||||||
struct testcase_t hs_service_tests[] = {
|
struct testcase_t hs_service_tests[] = {
|
||||||
{ "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
|
{ "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
@ -2194,7 +2545,7 @@ struct testcase_t hs_service_tests[] = {
|
|||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "rdv_circuit_opened", test_rdv_circuit_opened, TT_FORK,
|
{ "rdv_circuit_opened", test_rdv_circuit_opened, TT_FORK,
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "introduce2", test_introduce2, TT_FORK,
|
{ "bad_introduce2", test_bad_introduce2, TT_FORK,
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
{ "service_event", test_service_event, TT_FORK,
|
{ "service_event", test_service_event, TT_FORK,
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
@ -2212,6 +2563,7 @@ struct testcase_t hs_service_tests[] = {
|
|||||||
TT_FORK, NULL, NULL },
|
TT_FORK, NULL, NULL },
|
||||||
{ "export_client_circuit_id", test_export_client_circuit_id, TT_FORK,
|
{ "export_client_circuit_id", test_export_client_circuit_id, TT_FORK,
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
|
{ "intro2_handling", test_intro2_handling, TT_FORK, NULL, NULL },
|
||||||
|
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
@ -4571,6 +4571,35 @@ test_util_di_ops(void *arg)
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_util_memcpy_iftrue_timei(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
char buf1[25];
|
||||||
|
char buf2[25];
|
||||||
|
char buf3[25];
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
crypto_rand(buf1, sizeof(buf1));
|
||||||
|
crypto_rand(buf2, sizeof(buf2));
|
||||||
|
memcpy(buf3, buf1, sizeof(buf1));
|
||||||
|
|
||||||
|
/* We just copied buf1 into buf3. Now we're going to copy buf2 into buf2,
|
||||||
|
iff our coin flip comes up heads. */
|
||||||
|
bool coinflip = crypto_rand_int(2) == 0;
|
||||||
|
|
||||||
|
memcpy_if_true_timei(coinflip, buf3, buf2, sizeof(buf3));
|
||||||
|
|
||||||
|
if (coinflip) {
|
||||||
|
tt_mem_op(buf3, OP_EQ, buf2, sizeof(buf2));
|
||||||
|
} else {
|
||||||
|
tt_mem_op(buf3, OP_EQ, buf1, sizeof(buf1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_util_di_map(void *arg)
|
test_util_di_map(void *arg)
|
||||||
{
|
{
|
||||||
@ -6386,6 +6415,7 @@ struct testcase_t util_tests[] = {
|
|||||||
UTIL_LEGACY(path_is_relative),
|
UTIL_LEGACY(path_is_relative),
|
||||||
UTIL_LEGACY(strtok),
|
UTIL_LEGACY(strtok),
|
||||||
UTIL_LEGACY(di_ops),
|
UTIL_LEGACY(di_ops),
|
||||||
|
UTIL_TEST(memcpy_iftrue_timei, 0),
|
||||||
UTIL_TEST(di_map, 0),
|
UTIL_TEST(di_map, 0),
|
||||||
UTIL_TEST(round_to_next_multiple_of, 0),
|
UTIL_TEST(round_to_next_multiple_of, 0),
|
||||||
UTIL_TEST(laplace, 0),
|
UTIL_TEST(laplace, 0),
|
||||||
|
Loading…
Reference in New Issue
Block a user