#include "core/or/or.h"
#include "app/config/config.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/rend/rendservice.h"
#include "feature/hs_common/replaycache.h"
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_ob.h"
#include "core/crypto/hs_ntor.h"
#include "core/or/origin_circuit_st.h"
#include "trunnel/ed25519_cert.h"
#include "trunnel/hs/cell_common.h"
#include "trunnel/hs/cell_establish_intro.h"
#include "trunnel/hs/cell_introduce1.h"
#include "trunnel/hs/cell_rendezvous.h"
static void
compute_introduce_mac(const uint8_t *encoded_cell, size_t encoded_cell_len,
const uint8_t *encrypted, size_t encrypted_len,
const uint8_t *mac_key, size_t mac_key_len,
uint8_t *mac_out, size_t mac_out_len)
{
size_t offset = 0;
size_t mac_msg_len;
uint8_t mac_msg[RELAY_PAYLOAD_SIZE] = {0};
tor_assert(encoded_cell);
tor_assert(encrypted);
tor_assert(mac_key);
tor_assert(mac_out);
tor_assert(mac_out_len >= DIGEST256_LEN);
mac_msg_len = encoded_cell_len + (encrypted_len - DIGEST256_LEN);
tor_assert(mac_msg_len <= sizeof(mac_msg));
memcpy(mac_msg, encoded_cell, encoded_cell_len);
offset += encoded_cell_len;
memcpy(mac_msg + offset, encrypted, (encrypted_len - DIGEST256_LEN));
offset += (encrypted_len - DIGEST256_LEN);
tor_assert(offset == mac_msg_len);
crypto_mac_sha3_256(mac_out, mac_out_len,
mac_key, mac_key_len,
mac_msg, mac_msg_len);
memwipe(mac_msg, 0, sizeof(mac_msg));
}
static hs_ntor_intro_cell_keys_t *
get_introduce2_key_material(const ed25519_public_key_t *auth_key,
const curve25519_keypair_t *enc_key,
size_t n_subcredentials,
const hs_subcredential_t *subcredentials,
const uint8_t *encrypted_section,
curve25519_public_key_t *client_pk)
{
hs_ntor_intro_cell_keys_t *keys;
tor_assert(auth_key);
tor_assert(enc_key);
tor_assert(n_subcredentials > 0);
tor_assert(subcredentials);
tor_assert(encrypted_section);
tor_assert(client_pk);
keys = tor_calloc(n_subcredentials, sizeof(hs_ntor_intro_cell_keys_t));
memcpy(client_pk->public_key, encrypted_section, CURVE25519_PUBKEY_LEN);
if (hs_ntor_service_get_introduce1_keys_multi(auth_key, enc_key, client_pk,
n_subcredentials,
subcredentials, keys) < 0) {
memwipe(client_pk, 0, sizeof(curve25519_public_key_t));
tor_free(keys);
keys = NULL;
}
return keys;
}
static uint8_t *
decrypt_introduce2(const uint8_t *enc_key, const uint8_t *encrypted_section,
size_t encrypted_section_len)
{
uint8_t *decrypted = NULL;
crypto_cipher_t *cipher = NULL;
tor_assert(enc_key);
tor_assert(encrypted_section);
cipher = crypto_cipher_new_with_bits((char *) enc_key,
CURVE25519_PUBKEY_LEN * 8);
tor_assert(cipher);
decrypted = tor_malloc_zero(encrypted_section_len);
if (crypto_cipher_decrypt(cipher, (char *) decrypted,
(const char *) encrypted_section,
encrypted_section_len) < 0) {
tor_free(decrypted);
decrypted = NULL;
goto done;
}
done:
crypto_cipher_free(cipher);
return decrypted;
}
static trn_cell_introduce_encrypted_t *
parse_introduce2_encrypted(const uint8_t *decrypted_data,
size_t decrypted_len, const origin_circuit_t *circ,
const hs_service_t *service)
{
trn_cell_introduce_encrypted_t *enc_cell = NULL;
tor_assert(decrypted_data);
tor_assert(circ);
tor_assert(service);
if (trn_cell_introduce_encrypted_parse(&enc_cell, decrypted_data,
decrypted_len) < 0) {
log_info(LD_REND, "Unable to parse the decrypted ENCRYPTED section of "
"the INTRODUCE2 cell on circuit %u for service %s",
TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
goto err;
}
if (trn_cell_introduce_encrypted_get_onion_key_type(enc_cell) !=
TRUNNEL_HS_INTRO_ONION_KEY_TYPE_NTOR) {
log_info(LD_REND, "INTRODUCE2 onion key type is invalid. Got %u but "
"expected %u on circuit %u for service %s",
trn_cell_introduce_encrypted_get_onion_key_type(enc_cell),
TRUNNEL_HS_INTRO_ONION_KEY_TYPE_NTOR,
TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
goto err;
}
if (trn_cell_introduce_encrypted_getlen_onion_key(enc_cell) !=
CURVE25519_PUBKEY_LEN) {
log_info(LD_REND, "INTRODUCE2 onion key length is invalid. Got %u but "
"expected %d on circuit %u for service %s",
(unsigned)trn_cell_introduce_encrypted_getlen_onion_key(enc_cell),
CURVE25519_PUBKEY_LEN, TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
goto err;
}
return enc_cell;
err:
trn_cell_introduce_encrypted_free(enc_cell);
return NULL;
}
static ssize_t
build_legacy_establish_intro(const char *circ_nonce, crypto_pk_t *enc_key,
uint8_t *cell_out)
{
ssize_t cell_len;
tor_assert(circ_nonce);
tor_assert(enc_key);
tor_assert(cell_out);
memwipe(cell_out, 0, RELAY_PAYLOAD_SIZE);
cell_len = rend_service_encode_establish_intro_cell((char*)cell_out,
RELAY_PAYLOAD_SIZE,
enc_key, circ_nonce);
return cell_len;
}
static int
parse_introduce2_cell(const hs_service_t *service,
const origin_circuit_t *circ, const uint8_t *payload,
size_t payload_len,
trn_cell_introduce1_t **cell_ptr_out)
{
trn_cell_introduce1_t *cell = NULL;
tor_assert(service);
tor_assert(circ);
tor_assert(payload);
tor_assert(cell_ptr_out);
if (trn_cell_introduce1_parse(&cell, payload, payload_len) < 0) {
log_info(LD_PROTOCOL, "Unable to parse INTRODUCE2 cell on circuit %u "
"for service %s",
TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
goto err;
}
*cell_ptr_out = cell;
return 0;
err:
return -1;
}
static void
introduce1_set_encrypted_onion_key(trn_cell_introduce_encrypted_t *cell,
const uint8_t *onion_pk)
{
tor_assert(cell);
tor_assert(onion_pk);
trn_cell_introduce_encrypted_set_onion_key_type(cell,
TRUNNEL_HS_INTRO_ONION_KEY_TYPE_NTOR);
trn_cell_introduce_encrypted_set_onion_key_len(cell, CURVE25519_PUBKEY_LEN);
trn_cell_introduce_encrypted_setlen_onion_key(cell, CURVE25519_PUBKEY_LEN);
memcpy(trn_cell_introduce_encrypted_getarray_onion_key(cell), onion_pk,
trn_cell_introduce_encrypted_getlen_onion_key(cell));
}
static void
introduce1_set_encrypted_link_spec(trn_cell_introduce_encrypted_t *cell,
const smartlist_t *lspecs)
{
tor_assert(cell);
tor_assert(lspecs);
tor_assert(smartlist_len(lspecs) > 0);
tor_assert(smartlist_len(lspecs) <= UINT8_MAX);
uint8_t lspecs_num = (uint8_t) smartlist_len(lspecs);
trn_cell_introduce_encrypted_set_nspec(cell, lspecs_num);
SMARTLIST_FOREACH(lspecs, link_specifier_t *, ls,
trn_cell_introduce_encrypted_add_nspecs(cell, ls));
}
static void
introduce1_set_encrypted_padding(const trn_cell_introduce1_t *cell,
trn_cell_introduce_encrypted_t *enc_cell)
{
tor_assert(cell);
tor_assert(enc_cell);
ssize_t full_len = trn_cell_introduce1_encoded_len(cell) +
trn_cell_introduce_encrypted_encoded_len(enc_cell);
tor_assert(full_len > 0);
if (full_len < HS_CELL_INTRODUCE1_MIN_SIZE) {
size_t padding = HS_CELL_INTRODUCE1_MIN_SIZE - full_len;
trn_cell_introduce_encrypted_setlen_pad(enc_cell, padding);
memset(trn_cell_introduce_encrypted_getarray_pad(enc_cell), 0,
trn_cell_introduce_encrypted_getlen_pad(enc_cell));
}
}
static void
introduce1_encrypt_and_encode(trn_cell_introduce1_t *cell,
const trn_cell_introduce_encrypted_t *enc_cell,
const hs_cell_introduce1_data_t *data)
{
size_t offset = 0;
ssize_t encrypted_len;
ssize_t encoded_cell_len, encoded_enc_cell_len;
uint8_t encoded_cell[RELAY_PAYLOAD_SIZE] = {0};
uint8_t encoded_enc_cell[RELAY_PAYLOAD_SIZE] = {0};
uint8_t *encrypted = NULL;
uint8_t mac[DIGEST256_LEN];
crypto_cipher_t *cipher = NULL;
hs_ntor_intro_cell_keys_t keys;
tor_assert(cell);
tor_assert(enc_cell);
tor_assert(data);
encoded_cell_len = trn_cell_introduce1_encode(encoded_cell,
sizeof(encoded_cell), cell);
tor_assert(encoded_cell_len > 0);
encoded_enc_cell_len =
trn_cell_introduce_encrypted_encode(encoded_enc_cell,
sizeof(encoded_enc_cell), enc_cell);
tor_assert(encoded_enc_cell_len > 0);
if (hs_ntor_client_get_introduce1_keys(data->auth_pk, data->enc_pk,
data->client_kp,
data->subcredential, &keys) < 0) {
tor_assert_unreached();
}
cipher = crypto_cipher_new_with_bits((const char *) keys.enc_key,
sizeof(keys.enc_key) * 8);
tor_assert(cipher);
encrypted_len = sizeof(data->client_kp->pubkey) + encoded_enc_cell_len +
sizeof(mac);
tor_assert(encrypted_len < RELAY_PAYLOAD_SIZE);
encrypted = tor_malloc_zero(encrypted_len);
memcpy(encrypted, data->client_kp->pubkey.public_key,
sizeof(data->client_kp->pubkey.public_key));
offset += sizeof(data->client_kp->pubkey.public_key);
crypto_cipher_encrypt(cipher, (char *) encrypted + offset,
(const char *) encoded_enc_cell, encoded_enc_cell_len);
crypto_cipher_free(cipher);
offset += encoded_enc_cell_len;
compute_introduce_mac(encoded_cell, encoded_cell_len,
encrypted, encrypted_len,
keys.mac_key, sizeof(keys.mac_key),
mac, sizeof(mac));
memcpy(encrypted + offset, mac, sizeof(mac));
offset += sizeof(mac);
tor_assert(offset == (size_t) encrypted_len);
trn_cell_introduce1_setlen_encrypted(cell, encrypted_len);
memcpy(trn_cell_introduce1_getarray_encrypted(cell),
encrypted, encrypted_len);
memwipe(&keys, 0, sizeof(keys));
memwipe(mac, 0, sizeof(mac));
memwipe(encrypted, 0, sizeof(encrypted_len));
memwipe(encoded_enc_cell, 0, sizeof(encoded_enc_cell));
tor_free(encrypted);
}
static void
introduce1_set_encrypted(trn_cell_introduce1_t *cell,
const hs_cell_introduce1_data_t *data)
{
trn_cell_introduce_encrypted_t *enc_cell;
trn_cell_extension_t *ext;
tor_assert(cell);
tor_assert(data);
enc_cell = trn_cell_introduce_encrypted_new();
tor_assert(enc_cell);
ext = trn_cell_extension_new();
tor_assert(ext);
trn_cell_extension_set_num(ext, 0);
trn_cell_introduce_encrypted_set_extensions(enc_cell, ext);
memcpy(trn_cell_introduce_encrypted_getarray_rend_cookie(enc_cell),
data->rendezvous_cookie, REND_COOKIE_LEN);
introduce1_set_encrypted_onion_key(enc_cell, data->onion_pk->public_key);
introduce1_set_encrypted_link_spec(enc_cell, data->link_specifiers);
introduce1_set_encrypted_padding(cell, enc_cell);
introduce1_encrypt_and_encode(cell, enc_cell, data);
trn_cell_introduce_encrypted_free(enc_cell);
}
static void
introduce1_set_auth_key(trn_cell_introduce1_t *cell,
const hs_cell_introduce1_data_t *data)
{
tor_assert(cell);
tor_assert(data);
trn_cell_introduce1_set_auth_key_type(cell,
TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519);
trn_cell_introduce1_set_auth_key_len(cell, ED25519_PUBKEY_LEN);
trn_cell_introduce1_setlen_auth_key(cell, ED25519_PUBKEY_LEN);
memcpy(trn_cell_introduce1_getarray_auth_key(cell),
data->auth_pk->pubkey, trn_cell_introduce1_getlen_auth_key(cell));
}
static void
introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
const hs_cell_introduce1_data_t *data)
{
tor_assert(cell);
tor_assert(data);
if (data->is_legacy) {
uint8_t digest[DIGEST_LEN];
if (BUG(crypto_pk_get_digest(data->legacy_key, (char *) digest) < 0)) {
return;
}
memcpy(trn_cell_introduce1_getarray_legacy_key_id(cell),
digest, trn_cell_introduce1_getlen_legacy_key_id(cell));
} else {
memset(trn_cell_introduce1_getarray_legacy_key_id(cell), 0,
trn_cell_introduce1_getlen_legacy_key_id(cell));
}
}
static void
build_establish_intro_dos_param(trn_cell_extension_dos_t *dos_ext,
uint8_t param_type, uint64_t param_value)
{
trn_cell_extension_dos_param_t *dos_param =
trn_cell_extension_dos_param_new();
tor_assert(param_type == TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC ||
param_type == TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
trn_cell_extension_dos_param_set_type(dos_param, param_type);
trn_cell_extension_dos_param_set_value(dos_param, param_value);
trn_cell_extension_dos_add_params(dos_ext, dos_param);
}
static int
build_establish_intro_dos_extension(const hs_service_config_t *service_config,
trn_cell_extension_t *extensions)
{
ssize_t ret;
size_t dos_ext_encoded_len;
uint8_t *field_array;
trn_cell_extension_field_t *field = NULL;
trn_cell_extension_dos_t *dos_ext = NULL;
tor_assert(service_config);
tor_assert(extensions);
field = trn_cell_extension_field_new();
trn_cell_extension_field_set_field_type(field,
TRUNNEL_CELL_EXTENSION_TYPE_DOS);
dos_ext = trn_cell_extension_dos_new();
trn_cell_extension_dos_set_n_params(dos_ext, 2);
build_establish_intro_dos_param(dos_ext,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC,
service_config->intro_dos_rate_per_sec);
build_establish_intro_dos_param(dos_ext,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC,
service_config->intro_dos_burst_per_sec);
ret = trn_cell_extension_dos_encoded_len(dos_ext);
if (BUG(ret <= 0)) {
goto err;
}
dos_ext_encoded_len = ret;
trn_cell_extension_field_set_field_len(field, dos_ext_encoded_len);
trn_cell_extension_field_setlen_field(field, dos_ext_encoded_len);
field_array = trn_cell_extension_field_getarray_field(field);
ret = trn_cell_extension_dos_encode(field_array,
trn_cell_extension_field_getlen_field(field), dos_ext);
if (BUG(ret <= 0)) {
goto err;
}
tor_assert(ret == (ssize_t) dos_ext_encoded_len);
trn_cell_extension_add_fields(extensions, field);
trn_cell_extension_set_num(extensions,
trn_cell_extension_get_num(extensions) + 1);
trn_cell_extension_dos_free(dos_ext);
return 0;
err:
trn_cell_extension_field_free(field);
trn_cell_extension_dos_free(dos_ext);
return -1;
}
STATIC trn_cell_extension_t *
build_establish_intro_extensions(const hs_service_config_t *service_config,
const hs_service_intro_point_t *ip)
{
int ret;
trn_cell_extension_t *extensions;
tor_assert(service_config);
tor_assert(ip);
extensions = trn_cell_extension_new();
trn_cell_extension_set_num(extensions, 0);
if (service_config->has_dos_defense_enabled &&
ip->support_intro2_dos_defense) {
ret = build_establish_intro_dos_extension(service_config, extensions);
if (ret < 0) {
goto end;
}
}
end:
return extensions;
}
ssize_t
hs_cell_build_establish_intro(const char *circ_nonce,
const hs_service_config_t *service_config,
const hs_service_intro_point_t *ip,
uint8_t *cell_out)
{
ssize_t cell_len = -1;
uint16_t sig_len = ED25519_SIG_LEN;
trn_cell_establish_intro_t *cell = NULL;
trn_cell_extension_t *extensions;
tor_assert(circ_nonce);
tor_assert(service_config);
tor_assert(ip);
if (ip->base.is_only_legacy) {
tor_assert(ip->legacy_key);
cell_len = build_legacy_establish_intro(circ_nonce, ip->legacy_key,
cell_out);
tor_assert(cell_len <= RELAY_PAYLOAD_SIZE);
goto done;
}
extensions = build_establish_intro_extensions(service_config, ip);
cell = trn_cell_establish_intro_new();
trn_cell_establish_intro_set_extensions(cell, extensions);
trn_cell_establish_intro_set_sig_len(cell, sig_len);
trn_cell_establish_intro_setlen_sig(cell, sig_len);
trn_cell_establish_intro_set_auth_key_type(cell,
TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519);
{
uint16_t auth_key_len = ED25519_PUBKEY_LEN;
trn_cell_establish_intro_set_auth_key_len(cell, auth_key_len);
trn_cell_establish_intro_setlen_auth_key(cell, auth_key_len);
uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell);
memcpy(auth_key_ptr, ip->auth_key_kp.pubkey.pubkey, auth_key_len);
}
{
ssize_t tmp_cell_enc_len = 0;
ssize_t tmp_cell_mac_offset =
sig_len + sizeof(cell->sig_len) +
trn_cell_establish_intro_getlen_handshake_mac(cell);
uint8_t tmp_cell_enc[RELAY_PAYLOAD_SIZE] = {0};
uint8_t mac[TRUNNEL_SHA3_256_LEN], *handshake_ptr;
tmp_cell_enc_len = trn_cell_establish_intro_encode(tmp_cell_enc,
sizeof(tmp_cell_enc),
cell);
if (BUG(tmp_cell_enc_len < 0)) {
goto done;
}
tor_assert(tmp_cell_enc_len > tmp_cell_mac_offset);
crypto_mac_sha3_256(mac, sizeof(mac),
(uint8_t *) circ_nonce, DIGEST_LEN,
tmp_cell_enc, tmp_cell_enc_len - tmp_cell_mac_offset);
handshake_ptr = trn_cell_establish_intro_getarray_handshake_mac(cell);
memcpy(handshake_ptr, mac, sizeof(mac));
memwipe(mac, 0, sizeof(mac));
memwipe(tmp_cell_enc, 0, sizeof(tmp_cell_enc));
}
{
ssize_t tmp_cell_enc_len = 0;
ssize_t tmp_cell_sig_offset = (sig_len + sizeof(cell->sig_len));
uint8_t tmp_cell_enc[RELAY_PAYLOAD_SIZE] = {0}, *sig_ptr;
ed25519_signature_t sig;
tmp_cell_enc_len = trn_cell_establish_intro_encode(tmp_cell_enc,
sizeof(tmp_cell_enc),
cell);
if (BUG(tmp_cell_enc_len < 0)) {
goto done;
}
if (ed25519_sign_prefixed(&sig, tmp_cell_enc,
tmp_cell_enc_len - tmp_cell_sig_offset,
ESTABLISH_INTRO_SIG_PREFIX, &ip->auth_key_kp)) {
log_warn(LD_BUG, "Unable to make signature for ESTABLISH_INTRO cell.");
goto done;
}
sig_ptr = trn_cell_establish_intro_getarray_sig(cell);
memcpy(sig_ptr, sig.sig, sig_len);
memwipe(tmp_cell_enc, 0, sizeof(tmp_cell_enc));
}
cell_len = trn_cell_establish_intro_encode(cell_out, RELAY_PAYLOAD_SIZE,
cell);
done:
trn_cell_establish_intro_free(cell);
return cell_len;
}
ssize_t
hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
{
ssize_t ret;
trn_cell_intro_established_t *cell = NULL;
tor_assert(payload);
ret = trn_cell_intro_established_parse(&cell, payload, payload_len);
if (ret >= 0) {
trn_cell_intro_established_free(cell);
}
return ret;
}
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;
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;
}
if (BUG(encrypted_section_len < DIGEST256_LEN)) {
return NULL;
}
intro_keys_result = tor_malloc_zero(sizeof(*intro_keys_result));
for (unsigned i = 0; i < data->n_subcredentials; ++i) {
uint8_t mac[DIGEST256_LEN];
size_t mac_offset = encrypted_section_len - sizeof(mac);
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));
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));
}
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);
}
return intro_keys_result;
}
ssize_t
hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
const origin_circuit_t *circ,
const hs_service_t *service)
{
int ret = -1;
time_t elapsed;
uint8_t *decrypted = NULL;
size_t encrypted_section_len;
const uint8_t *encrypted_section;
trn_cell_introduce1_t *cell = NULL;
trn_cell_introduce_encrypted_t *enc_cell = NULL;
hs_ntor_intro_cell_keys_t *intro_keys = NULL;
tor_assert(data);
tor_assert(circ);
tor_assert(service);
if (parse_introduce2_cell(service, circ, data->payload, data->payload_len,
&cell) < 0) {
goto done;
}
log_info(LD_REND, "Received a decodable INTRODUCE2 cell on circuit %u "
"for service %s. Decoding encrypted section...",
TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
encrypted_section = trn_cell_introduce1_getconstarray_encrypted(cell);
encrypted_section_len = trn_cell_introduce1_getlen_encrypted(cell);
if (encrypted_section_len < (CURVE25519_PUBKEY_LEN + DIGEST256_LEN)) {
log_info(LD_REND, "Invalid INTRODUCE2 encrypted section length "
"for service %s. Dropping cell.",
safe_str_client(service->onion_address));
goto done;
}
if (replaycache_add_test_and_elapsed(data->replay_cache, encrypted_section,
encrypted_section_len, &elapsed)) {
log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the "
"same ENCRYPTED section was seen %ld seconds ago. "
"Dropping cell.", (long int) elapsed);
goto done;
}
memcpy(&data->client_pk.public_key, encrypted_section,
CURVE25519_PUBKEY_LEN);
intro_keys = get_introduce2_keys_and_verify_mac(data, encrypted_section,
encrypted_section_len);
if (!intro_keys) {
log_warn(LD_REND, "Could not get valid INTRO2 keys on circuit %u "
"for service %s", TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
goto done;
}
{
const uint8_t *encrypted_data =
encrypted_section + sizeof(data->client_pk);
size_t encrypted_data_len =
encrypted_section_len - (sizeof(data->client_pk) + DIGEST256_LEN);
decrypted = decrypt_introduce2(intro_keys->enc_key,
encrypted_data, encrypted_data_len);
if (decrypted == NULL) {
log_info(LD_REND, "Unable to decrypt the ENCRYPTED section of an "
"INTRODUCE2 cell on circuit %u for service %s",
TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
goto done;
}
enc_cell = parse_introduce2_encrypted(decrypted, encrypted_data_len,
circ, service);
memwipe(decrypted, 0, encrypted_data_len);
if (enc_cell == NULL) {
goto done;
}
}
memcpy(data->onion_pk.public_key,
trn_cell_introduce_encrypted_getconstarray_onion_key(enc_cell),
CURVE25519_PUBKEY_LEN);
memcpy(data->rendezvous_cookie,
trn_cell_introduce_encrypted_getconstarray_rend_cookie(enc_cell),
sizeof(data->rendezvous_cookie));
for (size_t idx = 0;
idx < trn_cell_introduce_encrypted_get_nspec(enc_cell); idx++) {
link_specifier_t *lspec =
trn_cell_introduce_encrypted_get_nspecs(enc_cell, idx);
if (BUG(!lspec)) {
goto done;
}
link_specifier_t *lspec_dup = link_specifier_dup(lspec);
if (BUG(!lspec_dup)) {
goto done;
}
smartlist_add(data->link_specifiers, lspec_dup);
}
ret = 0;
log_info(LD_REND, "Valid INTRODUCE2 cell. Launching rendezvous circuit.");
done:
if (intro_keys) {
memwipe(intro_keys, 0, sizeof(hs_ntor_intro_cell_keys_t));
tor_free(intro_keys);
}
tor_free(decrypted);
trn_cell_introduce_encrypted_free(enc_cell);
trn_cell_introduce1_free(cell);
return ret;
}
ssize_t
hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
size_t rendezvous_cookie_len,
const uint8_t *rendezvous_handshake_info,
size_t rendezvous_handshake_info_len,
uint8_t *cell_out)
{
ssize_t cell_len;
trn_cell_rendezvous1_t *cell;
tor_assert(rendezvous_cookie);
tor_assert(rendezvous_handshake_info);
tor_assert(cell_out);
cell = trn_cell_rendezvous1_new();
memcpy(trn_cell_rendezvous1_getarray_rendezvous_cookie(cell),
rendezvous_cookie, rendezvous_cookie_len);
trn_cell_rendezvous1_setlen_handshake_info(cell,
rendezvous_handshake_info_len);
memcpy(trn_cell_rendezvous1_getarray_handshake_info(cell),
rendezvous_handshake_info, rendezvous_handshake_info_len);
cell_len = trn_cell_rendezvous1_encode(cell_out, RELAY_PAYLOAD_SIZE, cell);
tor_assert(cell_len > 0);
trn_cell_rendezvous1_free(cell);
return cell_len;
}
ssize_t
hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
uint8_t *cell_out)
{
ssize_t cell_len;
trn_cell_introduce1_t *cell;
trn_cell_extension_t *ext;
tor_assert(data);
tor_assert(cell_out);
cell = trn_cell_introduce1_new();
tor_assert(cell);
ext = trn_cell_extension_new();
tor_assert(ext);
trn_cell_extension_set_num(ext, 0);
trn_cell_introduce1_set_extensions(cell, ext);
introduce1_set_legacy_id(cell, data);
introduce1_set_auth_key(cell, data);
introduce1_set_encrypted(cell, data);
cell_len = trn_cell_introduce1_encode(cell_out, RELAY_PAYLOAD_SIZE, cell);
trn_cell_introduce1_free(cell);
return cell_len;
}
ssize_t
hs_cell_build_establish_rendezvous(const uint8_t *rendezvous_cookie,
uint8_t *cell_out)
{
tor_assert(rendezvous_cookie);
tor_assert(cell_out);
memcpy(cell_out, rendezvous_cookie, HS_REND_COOKIE_LEN);
return HS_REND_COOKIE_LEN;
}
int
hs_cell_parse_introduce_ack(const uint8_t *payload, size_t payload_len)
{
int ret = -1;
trn_cell_introduce_ack_t *cell = NULL;
tor_assert(payload);
if (payload_len <= 1) {
if (payload_len == 0) {
ret = TRUNNEL_HS_INTRO_ACK_STATUS_SUCCESS;
} else {
ret = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
}
goto end;
}
if (trn_cell_introduce_ack_parse(&cell, payload, payload_len) < 0) {
log_info(LD_REND, "Invalid INTRODUCE_ACK cell. Unable to parse it.");
goto end;
}
ret = trn_cell_introduce_ack_get_status(cell);
end:
trn_cell_introduce_ack_free(cell);
return ret;
}
int
hs_cell_parse_rendezvous2(const uint8_t *payload, size_t payload_len,
uint8_t *handshake_info, size_t handshake_info_len)
{
int ret = -1;
trn_cell_rendezvous2_t *cell = NULL;
tor_assert(payload);
tor_assert(handshake_info);
if (trn_cell_rendezvous2_parse(&cell, payload, payload_len) < 0) {
log_info(LD_REND, "Invalid RENDEZVOUS2 cell. Unable to parse it.");
goto end;
}
tor_assert(trn_cell_rendezvous2_getlen_handshake_info(cell) ==
handshake_info_len);
memcpy(handshake_info,
trn_cell_rendezvous2_getconstarray_handshake_info(cell),
handshake_info_len);
ret = 0;
end:
trn_cell_rendezvous2_free(cell);
return ret;
}
void
hs_cell_introduce1_data_clear(hs_cell_introduce1_data_t *data)
{
if (data == NULL) {
return;
}
smartlist_free(data->link_specifiers);
memwipe(data, 0, sizeof(hs_cell_introduce1_data_t));
}