📄 rendcommon.c
字号:
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char rendcommon_c_id[] =
"$Id$";
/**
* \file rendcommon.c
* \brief Rendezvous implementation: shared code between
* introducers, services, clients, and rendezvous points.
**/
#include "or.h"
/** Return 0 if one and two are the same service ids, else -1 or 1 */
int
rend_cmp_service_ids(const char *one, const char *two)
{
return strcasecmp(one,two);
}
/** Free the storage held by the service descriptor <b>desc</b>.
*/
void
rend_service_descriptor_free(rend_service_descriptor_t *desc)
{
if (desc->pk)
crypto_free_pk_env(desc->pk);
if (desc->intro_nodes) {
SMARTLIST_FOREACH(desc->intro_nodes, rend_intro_point_t *, intro,
rend_intro_point_free(intro););
smartlist_free(desc->intro_nodes);
}
tor_free(desc);
}
/** Length of the descriptor cookie that is used for versioned hidden
* service descriptors. */
#define REND_DESC_COOKIE_LEN 16
/** Length of the replica number that is used to determine the secret ID
* part of versioned hidden service descriptors. */
#define REND_REPLICA_LEN 1
/** Compute the descriptor ID for <b>service_id</b> of length
* <b>REND_SERVICE_ID_LEN</b> and <b>secret_id_part</b> of length
* <b>DIGEST_LEN</b>, and write it to <b>descriptor_id_out</b> of length
* <b>DIGEST_LEN</b>. */
void
rend_get_descriptor_id_bytes(char *descriptor_id_out,
const char *service_id,
const char *secret_id_part)
{
crypto_digest_env_t *digest = crypto_new_digest_env();
crypto_digest_add_bytes(digest, service_id, REND_SERVICE_ID_LEN);
crypto_digest_add_bytes(digest, secret_id_part, DIGEST_LEN);
crypto_digest_get_digest(digest, descriptor_id_out, DIGEST_LEN);
crypto_free_digest_env(digest);
}
/** Compute the secret ID part for time_period,
* a <b>descriptor_cookie</b> of length
* <b>REND_DESC_COOKIE_LEN</b> which may also be <b>NULL</b> if no
* descriptor_cookie shall be used, and <b>replica</b>, and write it to
* <b>secret_id_part</b> of length DIGEST_LEN. */
static void
get_secret_id_part_bytes(char *secret_id_part, uint32_t time_period,
const char *descriptor_cookie, uint8_t replica)
{
crypto_digest_env_t *digest = crypto_new_digest_env();
time_period = htonl(time_period);
crypto_digest_add_bytes(digest, (char*)&time_period, sizeof(uint32_t));
if (descriptor_cookie) {
crypto_digest_add_bytes(digest, descriptor_cookie,
REND_DESC_COOKIE_LEN);
}
crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_LEN);
crypto_digest_get_digest(digest, secret_id_part, DIGEST_LEN);
crypto_free_digest_env(digest);
}
/** Return the time period for time <b>now</b> plus a potentially
* intended <b>deviation</b> of one or more periods, based on the first byte
* of <b>service_id</b>. */
static uint32_t
get_time_period(time_t now, uint8_t deviation, const char *service_id)
{
/* The time period is the number of REND_TIME_PERIOD_V2_DESC_VALIDITY
* intervals that have passed since the epoch, offset slightly so that
* each service's time periods start and end at a fraction of that
* period based on their first byte. */
return (uint32_t)
(now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256)
/ REND_TIME_PERIOD_V2_DESC_VALIDITY + deviation;
}
/** Compute the time in seconds that a descriptor that is generated
* <b>now</b> for <b>service_id</b> will be valid. */
static uint32_t
get_seconds_valid(time_t now, const char *service_id)
{
uint32_t result = REND_TIME_PERIOD_V2_DESC_VALIDITY -
((uint32_t)
(now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256)
% REND_TIME_PERIOD_V2_DESC_VALIDITY);
return result;
}
/** Compute the binary <b>desc_id_out</b> (DIGEST_LEN bytes long) for a given
* base32-encoded <b>service_id</b> and optional unencoded
* <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
* at time <b>now</b> for replica number
* <b>replica</b>. <b>desc_id</b> needs to have <b>DIGEST_LEN</b> bytes
* free. Return 0 for success, -1 otherwise. */
int
rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
const char *descriptor_cookie, time_t now,
uint8_t replica)
{
char service_id_binary[REND_SERVICE_ID_LEN];
char secret_id_part[DIGEST_LEN];
uint32_t time_period;
if (!service_id ||
strlen(service_id) != REND_SERVICE_ID_LEN_BASE32) {
log_warn(LD_REND, "Could not compute v2 descriptor ID: "
"Illegal service ID: %s", safe_str(service_id));
return -1;
}
if (replica >= REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS) {
log_warn(LD_REND, "Could not compute v2 descriptor ID: "
"Replica number out of range: %d", replica);
return -1;
}
/* Convert service ID to binary. */
if (base32_decode(service_id_binary, REND_SERVICE_ID_LEN,
service_id, REND_SERVICE_ID_LEN_BASE32) < 0) {
log_warn(LD_REND, "Could not compute v2 descriptor ID: "
"Illegal characters in service ID: %s",
safe_str(service_id));
return -1;
}
/* Calculate current time-period. */
time_period = get_time_period(now, 0, service_id_binary);
/* Calculate secret-id-part = h(time-period + replica). */
get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
replica);
/* Calculate descriptor ID. */
rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part);
return 0;
}
/* Encode the introduction points in <b>desc</b>, optionally encrypt them with
* an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
* encode it in base64, and write it to a newly allocated string, and write a
* pointer to it to *<b>ipos_base64</b>. Return 0 for success, -1
* otherwise. */
static int
rend_encode_v2_intro_points(char **ipos_base64,
rend_service_descriptor_t *desc,
const char *descriptor_cookie)
{
size_t unenc_len;
char *unenc = NULL;
size_t unenc_written = 0;
int i;
int r = -1;
/* Assemble unencrypted list of introduction points. */
*ipos_base64 = NULL;
unenc_len = smartlist_len(desc->intro_nodes) * 1000; /* too long, but ok. */
unenc = tor_malloc_zero(unenc_len);
for (i = 0; i < smartlist_len(desc->intro_nodes); i++) {
char id_base32[REND_INTRO_POINT_ID_LEN_BASE32 + 1];
char *onion_key = NULL;
size_t onion_key_len;
crypto_pk_env_t *intro_key;
char *service_key = NULL;
char *address = NULL;
size_t service_key_len;
int res;
rend_intro_point_t *intro = smartlist_get(desc->intro_nodes, i);
/* Obtain extend info with introduction point details. */
extend_info_t *info = intro->extend_info;
/* Encode introduction point ID. */
base32_encode(id_base32, sizeof(id_base32),
info->identity_digest, DIGEST_LEN);
/* Encode onion key. */
if (crypto_pk_write_public_key_to_string(info->onion_key, &onion_key,
&onion_key_len) < 0) {
log_warn(LD_REND, "Could not write onion key.");
goto done;
}
/* Encode intro key. */
intro_key = intro->intro_key;
if (!intro_key ||
crypto_pk_write_public_key_to_string(intro_key, &service_key,
&service_key_len) < 0) {
log_warn(LD_REND, "Could not write intro key.");
tor_free(onion_key);
goto done;
}
/* Assemble everything for this introduction point. */
address = tor_dup_addr(info->addr);
res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written,
"introduction-point %s\n"
"ip-address %s\n"
"onion-port %d\n"
"onion-key\n%s"
"service-key\n%s",
id_base32,
address,
info->port,
onion_key,
service_key);
tor_free(address);
tor_free(onion_key);
tor_free(service_key);
if (res < 0) {
log_warn(LD_REND, "Not enough space for writing introduction point "
"string.");
goto done;
}
/* Update total number of written bytes for unencrypted intro points. */
unenc_written += res;
}
/* Finalize unencrypted introduction points. */
if (unenc_len < unenc_written + 2) {
log_warn(LD_REND, "Not enough space for finalizing introduction point "
"string.");
goto done;
}
unenc[unenc_written++] = '\n';
unenc[unenc_written++] = 0;
/* If a descriptor cookie is passed, encrypt introduction points. */
if (descriptor_cookie) {
char *enc = tor_malloc_zero(unenc_written + CIPHER_IV_LEN);
crypto_cipher_env_t *cipher =
crypto_create_init_cipher(descriptor_cookie, 1);
int enclen = crypto_cipher_encrypt_with_iv(cipher, enc,
unenc_written + CIPHER_IV_LEN,
unenc, unenc_written);
crypto_free_cipher_env(cipher);
if (enclen < 0) {
log_warn(LD_REND, "Could not encrypt introduction point string.");
tor_free(enc);
goto done;
}
/* Replace original string with the encrypted one. */
tor_free(unenc);
unenc = enc;
unenc_written = enclen;
}
/* Base64-encode introduction points. */
*ipos_base64 = tor_malloc_zero(unenc_written * 2);
if (base64_encode(*ipos_base64, unenc_written * 2, unenc, unenc_written)<0) {
log_warn(LD_REND, "Could not encode introduction point string to "
"base64.");
goto done;
}
r = 0;
done:
if (r<0)
tor_free(*ipos_base64);
tor_free(unenc);
return r;
}
/** Attempt to parse the given <b>desc_str</b> and return true if this
* succeeds, false otherwise. */
static int
rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc)
{
rend_service_descriptor_t *test_parsed = NULL;
char test_desc_id[DIGEST_LEN];
char *test_intro_content = NULL;
size_t test_intro_size;
size_t test_encoded_size;
const char *test_next;
int res = rend_parse_v2_service_descriptor(&test_parsed, test_desc_id,
&test_intro_content,
&test_intro_size,
&test_encoded_size,
&test_next, desc->desc_str);
if (test_parsed)
rend_service_descriptor_free(test_parsed);
tor_free(test_intro_content);
return (res >= 0);
}
/** Free the storage held by an encoded v2 service descriptor. */
void
rend_encoded_v2_service_descriptor_free(
rend_encoded_v2_service_descriptor_t *desc)
{
tor_free(desc->desc_str);
tor_free(desc);
}
/** Free the storage held by an introduction point info. */
void
rend_intro_point_free(rend_intro_point_t *intro)
{
if (intro->extend_info)
extend_info_free(intro->extend_info);
if (intro->intro_key)
crypto_free_pk_env(intro->intro_key);
tor_free(intro);
}
/** Encode a set of rend_encoded_v2_service_descriptor_t's for <b>desc</b>
* at time <b>now</b> using <b>descriptor_cookie</b> (may be <b>NULL</b> if
* introduction points shall not be encrypted) and <b>period</b> (e.g. 0
* for the current period, 1 for the next period, etc.) and add them to
* the existing list <b>descs_out</b>; return the number of seconds that
* the descriptors will be found by clients, or -1 if the encoding was not
* successful. */
int
rend_encode_v2_descriptors(smartlist_t *descs_out,
rend_service_descriptor_t *desc, time_t now,
const char *descriptor_cookie, uint8_t period)
{
char service_id[DIGEST_LEN];
uint32_t time_period;
char *ipos_base64 = NULL;
int k;
uint32_t seconds_valid;
if (!desc) {
log_warn(LD_REND, "Could not encode v2 descriptor: No desc given.");
return -1;
}
/* Obtain service_id from public key. */
crypto_pk_get_digest(desc->pk, service_id);
/* Calculate current time-period. */
time_period = get_time_period(now, period, service_id);
/* Determine how many seconds the descriptor will be valid. */
seconds_valid = period * REND_TIME_PERIOD_V2_DESC_VALIDITY +
get_seconds_valid(now, service_id);
/* Assemble, possibly encrypt, and encode introduction points. */
if (smartlist_len(desc->intro_nodes) > 0 &&
rend_encode_v2_intro_points(&ipos_base64, desc, descriptor_cookie) < 0) {
log_warn(LD_REND, "Encoding of introduction points did not succeed.");
return -1;
}
/* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */
for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
char secret_id_part[DIGEST_LEN];
char secret_id_part_base32[REND_SECRET_ID_PART_LEN_BASE32 + 1];
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
char *permanent_key = NULL;
size_t permanent_key_len;
char published[ISO_TIME_LEN+1];
int i;
char protocol_versions_string[16]; /* max len: "0,1,2,3,4,5,6,7\0" */
size_t protocol_versions_written;
size_t desc_len;
char *desc_str = NULL;
int result = 0;
size_t written = 0;
char desc_digest[DIGEST_LEN];
rend_encoded_v2_service_descriptor_t *enc =
tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
/* Calculate secret-id-part = h(time-period + cookie + replica). */
get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
k);
base32_encode(secret_id_part_base32, sizeof(secret_id_part_base32),
secret_id_part, DIGEST_LEN);
/* Calculate descriptor ID. */
rend_get_descriptor_id_bytes(enc->desc_id, service_id, secret_id_part);
base32_encode(desc_id_base32, sizeof(desc_id_base32),
enc->desc_id, DIGEST_LEN);
/* PEM-encode the public key */
if (crypto_pk_write_public_key_to_string(desc->pk, &permanent_key,
&permanent_key_len) < 0) {
log_warn(LD_BUG, "Could not write public key to string.");
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
/* Encode timestamp. */
format_iso_time(published, desc->timestamp);
/* Write protocol-versions bitmask to comma-separated value string. */
protocol_versions_written = 0;
for (i = 0; i < 8; i++) {
if (desc->protocols & 1 << i) {
tor_snprintf(protocol_versions_string + protocol_versions_written,
16 - protocol_versions_written, "%d,", i);
protocol_versions_written += 2;
}
}
if (protocol_versions_written)
protocol_versions_string[protocol_versions_written - 1] = '\0';
else
protocol_versions_string[0]= '\0';
/* Assemble complete descriptor. */
desc_len = 2000 + smartlist_len(desc->intro_nodes) * 1000; /* far too long,
but okay.*/
enc->desc_str = desc_str = tor_malloc_zero(desc_len);
result = tor_snprintf(desc_str, desc_len,
"rendezvous-service-descriptor %s\n"
"version 2\n"
"permanent-key\n%s"
"secret-id-part %s\n"
"publication-time %s\n"
"protocol-versions %s\n",
desc_id_base32,
permanent_key,
secret_id_part_base32,
published,
protocol_versions_string);
tor_free(permanent_key);
if (result < 0) {
log_warn(LD_BUG, "Descriptor ran out of room.");
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -