tor/src/or/hs_cell.c

186 lines
6.5 KiB
C
Raw Normal View History

/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_cell.c
* \brief Hidden service API for cell creation and handling.
**/
#include "or.h"
#include "rendservice.h"
#include "hs_cell.h"
/* Trunnel. */
#include "hs/cell_common.h"
#include "hs/cell_establish_intro.h"
/* Build a legacy ESTABLISH_INTRO cell with the given circuit nonce and RSA
* encryption key. The encoded cell is put in cell_out that MUST at least be
* of the size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on
* success else a negative value and cell_out is untouched. */
static ssize_t
build_legacy_establish_intro(const char *circ_nonce, crypto_pk_t *enc_key,
uint8_t *cell_out)
{
ssize_t cell_len;
char buf[RELAY_PAYLOAD_SIZE] = {0};
tor_assert(circ_nonce);
tor_assert(enc_key);
tor_assert(cell_out);
cell_len = rend_service_encode_establish_intro_cell(buf, sizeof(buf),
enc_key, circ_nonce);
tor_assert(cell_len <= RELAY_PAYLOAD_SIZE);
if (cell_len >= 0) {
memcpy(cell_out, buf, cell_len);
}
return cell_len;
}
/* ========== */
/* Public API */
/* ========== */
/* Build an ESTABLISH_INTRO cell with the given circuit nonce and intro point
* object. The encoded cell is put in cell_out that MUST at least be of the
* size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on success else
* a negative value and cell_out is untouched. This function also supports
* legacy cell creation. */
ssize_t
hs_cell_build_establish_intro(const char *circ_nonce,
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_extension_t *ext;
trn_cell_establish_intro_t *cell = NULL;
tor_assert(circ_nonce);
tor_assert(ip);
/* Quickly handle the legacy 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);
/* Success or not we are done here. */
goto done;
}
/* Set extension data. None used here. */
ext = trn_cell_extension_new();
trn_cell_extension_set_num(ext, 0);
cell = trn_cell_establish_intro_new();
trn_cell_establish_intro_set_extensions(cell, ext);
/* Set signature size. Array is then allocated in the cell. We need to do
* this early so we can use trunnel API to get the signature length. */
trn_cell_establish_intro_set_sig_len(cell, sig_len);
trn_cell_establish_intro_setlen_sig(cell, sig_len);
/* Set AUTH_KEY_TYPE: 2 means ed25519 */
trn_cell_establish_intro_set_auth_key_type(cell,
HS_INTRO_AUTH_KEY_TYPE_ED25519);
/* Set AUTH_KEY and AUTH_KEY_LEN field. Must also set byte-length of
* AUTH_KEY to match */
{
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);
/* We do this call _after_ setting the length because it's reallocated at
* that point only. */
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);
}
/* Calculate HANDSHAKE_AUTH field (MAC). */
{
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;
/* We first encode the current fields we have in the cell so we can
* compute the MAC using the raw bytes. */
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;
}
/* Sanity check. */
tor_assert(tmp_cell_enc_len > tmp_cell_mac_offset);
/* Circuit nonce is always DIGEST_LEN according to tor-spec.txt. */
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));
}
/* Calculate the cell signature SIG. */
{
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;
/* We first encode the current fields we have in the cell so we can
* compute the signature from the raw bytes of the cell. */
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;
}
/* Copy the signature into the cell. */
sig_ptr = trn_cell_establish_intro_getarray_sig(cell);
memcpy(sig_ptr, sig.sig, sig_len);
}
/* Encode the cell. Can't be bigger than a standard cell. */
cell_len = trn_cell_establish_intro_encode(cell_out, RELAY_PAYLOAD_SIZE,
cell);
done:
trn_cell_establish_intro_free(cell);
return cell_len;
}
/* Parse the INTRO_ESTABLISHED cell in the payload of size payload_len. If we
* are successful at parsing it, return the length of the parsed cell else a
* negative value on error. */
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);
/* Try to parse the payload into a cell making sure we do actually have a
* valid cell. */
ret = trn_cell_intro_established_parse(&cell, payload, payload_len);
if (ret >= 0) {
/* On success, we do not keep the cell, we just notify the caller that it
* was successfully parsed. */
trn_cell_intro_established_free(cell);
}
return ret;
}