Parse and generate service descriptors

svn:r1403
This commit is contained in:
Nick Mathewson 2004-03-31 02:07:38 +00:00
parent 670aeb6c8d
commit 6ea61d5e0d
8 changed files with 212 additions and 8 deletions

View File

@ -105,9 +105,9 @@ Rendezvous service:
can be in.) [NM] can be in.) [NM]
o Add circuit metadata [NM] 3 hours o Add circuit metadata [NM] 3 hours
- Code to configure hidden services [NM] 4 hours - Code to configure hidden services [NM] 4 hours
- Service descriptors . Service descriptors
- OPs need to maintain identity keys for hidden services [NM] 1 hour - OPs need to maintain identity keys for hidden services [NM] 1 hour
- Code to generate and parse service descriptors [NM] 4 hours o Code to generate and parse service descriptors [NM] 4 hours
- Advertisement - Advertisement
. Generate y.onion hostnames [NM] 1 hour . Generate y.onion hostnames [NM] 1 hour
- Code to do an HTTP connection over Tor from within Tor [RD] - Code to do an HTTP connection over Tor from within Tor [RD]

View File

@ -108,13 +108,13 @@ Tor Rendezvous Spec
KL Key length [2 octets] KL Key length [2 octets]
PK Bob's public key [KL octets] PK Bob's public key [KL octets]
TS A timestamp [8 octets] TS A timestamp [4 octets]
NI Number of introduction points [2 octets] NI Number of introduction points [2 octets]
Ipt A list of NUL-terminated OR nicknames [variable] Ipt A list of NUL-terminated OR nicknames [variable]
SIG Signature of above fields [KL octets] SIG Signature of above fields [KL octets]
KL is the length of PK, in octets. (Currently, KL must be 128.) KL is the length of PK, in octets. (Currently, KL must be 128.)
TS is the number of milliseconds elapsed since Jan 1, 1970. TS is the number of seconds elapsed since Jan 1, 1970.
[Shouldn't the nicknames be hostname:port's instead? That way, Alice's [Shouldn't the nicknames be hostname:port's instead? That way, Alice's
directory servers don't need to know Bob's chosen introduction points. directory servers don't need to know Bob's chosen introduction points.

View File

@ -569,6 +569,7 @@ int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b) {
} }
} }
/* return the size of the public key modulus in 'env', in bytes. */
int crypto_pk_keysize(crypto_pk_env_t *env) int crypto_pk_keysize(crypto_pk_env_t *env)
{ {
assert(env && env->key); assert(env && env->key);

View File

@ -49,13 +49,36 @@ void tor_strlower(char *s);
*/ */
#define get_uint16(cp) (*(uint16_t*)(cp)) #define get_uint16(cp) (*(uint16_t*)(cp))
#define get_uint32(cp) (*(uint32_t*)(cp)) #define get_uint32(cp) (*(uint32_t*)(cp))
#define set_uint16(cp,v) do { *(uint16_t)(cp) = (v) } while (0) #define set_uint16(cp,v) do { *(uint16_t*)(cp) = (v); } while (0)
#define set_uint32(cp,v) do { *(uint32_t)(cp) = (v) } while (0) #define set_uint32(cp,v) do { *(uint32_t*)(cp) = (v); } while (0)
#else #else
#if 1
uint16_t get_uint16(char *cp); uint16_t get_uint16(char *cp);
uint32_t get_uint32(char *cp); uint32_t get_uint32(char *cp);
void set_uint16(char *cp, uint16_t v); void set_uint16(char *cp, uint16_t v);
void set_uint32(char *cp, uint32_t v); void set_uint32(char *cp, uint32_t v);
#else
#define get_uint16(cp) \
( ((*(((uint8_t*)(cp))+0))<<8) + \
((*(((uint8_t*)(cp))+1)) ) )
#define get_uint32(cp) \
( ((*(((uint8_t*)(cp))+0))<<24) + \
((*(((uint8_t*)(cp))+1))<<16) + \
((*(((uint8_t*)(cp))+2))<<8 ) + \
((*(((uint8_t*)(cp))+3)) ) )
#define set_uint16(cp,v) \
do { \
*(((uint8_t*)(cp))+0) = (v >> 8)&0xff; \
*(((uint8_t*)(cp))+1) = (v >> 0)&0xff; \
} while (0)
#define set_uint32(cp,v) \
do { \
*(((uint8_t*)(cp))+0) = (v >> 24)&0xff; \
*(((uint8_t*)(cp))+1) = (v >> 16)&0xff; \
*(((uint8_t*)(cp))+2) = (v >> 8)&0xff; \
*(((uint8_t*)(cp))+3) = (v >> 0)&0xff; \
} while (0)
#endif
#endif #endif
typedef struct { typedef struct {

View File

@ -5,14 +5,14 @@ noinst_PROGRAMS = test
bin_PROGRAMS = tor bin_PROGRAMS = tor
tor_SOURCES = buffers.c circuit.c command.c connection.c \ tor_SOURCES = buffers.c circuit.c command.c connection.c \
connection_or.c config.c dirserv.c \ connection_or.c config.c dirserv.c rendcommon.c \
onion.c router.c routerlist.c directory.c dns.c connection_edge.c \ onion.c router.c routerlist.c directory.c dns.c connection_edge.c \
rephist.c cpuworker.c main.c tor_main.c rephist.c cpuworker.c main.c tor_main.c
tor_LDADD = ../common/libor.a tor_LDADD = ../common/libor.a
test_SOURCES = buffers.c circuit.c command.c connection.c \ test_SOURCES = buffers.c circuit.c command.c connection.c \
connection_or.c config.c dirserv.c \ connection_or.c config.c dirserv.c rendcommon.c \
onion.c router.c routerlist.c directory.c dns.c connection_edge.c \ onion.c router.c routerlist.c directory.c dns.c connection_edge.c \
rephist.c cpuworker.c main.c test.c rephist.c cpuworker.c main.c test.c

View File

@ -972,6 +972,20 @@ void rep_hist_dump_stats(time_t now, int severity);
/* length of 'y' portion of 'y.onion' URL. */ /* length of 'y' portion of 'y.onion' URL. */
#define REND_SERVICE_ID_LEN 16 #define REND_SERVICE_ID_LEN 16
typedef struct rend_service_descriptor_t {
crypto_pk_env_t *pk;
time_t timestamp;
int n_intro_points;
char **intro_points;
} rend_service_descriptor_t;
void rend_service_descriptor_free(rend_service_descriptor_t *desc);
int rend_encode_service_descriptor(rend_service_descriptor_t *desc,
crypto_pk_env_t *key,
char **str_out,
int *len_out);
rend_service_descriptor_t *rend_parse_service_descriptor(const char *str, int len);
#endif #endif
/* /*

130
src/or/rendcommon.c Normal file
View File

@ -0,0 +1,130 @@
/* Copyright 2004 Roger Dingledine */
/* See LICENSE for licensing information */
/* $Id$ */
#include "or.h"
void rend_service_descriptor_free(rend_service_descriptor_t *desc)
{
int i;
if (desc->pk)
crypto_free_pk_env(desc->pk);
if (desc->intro_points) {
for (i=0; i < desc->n_intro_points; ++i) {
tor_free(desc->intro_points[i]);
}
tor_free(desc->intro_points);
}
tor_free(desc);
}
int
rend_encode_service_descriptor(rend_service_descriptor_t *desc,
crypto_pk_env_t *key,
char **str_out, int *len_out)
{
char *buf, *cp, *ipoint;
int i, keylen, asn1len;
char digest[CRYPTO_SHA1_DIGEST_LEN];
keylen = crypto_pk_keysize(desc->pk);
buf = tor_malloc(keylen*2); /* XXXX */
asn1len = crypto_pk_asn1_encode(desc->pk, buf, keylen*2);
if (asn1len<0) {
tor_free(buf);
return -1;
}
*len_out = 2 + asn1len + 4 + 2 + keylen;
for (i = 0; i < desc->n_intro_points; ++i) {
*len_out += strlen(desc->intro_points[i]) + 1;
}
cp = *str_out = tor_malloc(*len_out);
set_uint16(cp, (uint16_t)asn1len);
cp += 2;
memcpy(cp, buf, asn1len);
tor_free(buf);
cp += asn1len;
set_uint32(cp, (uint32_t)desc->timestamp);
cp += 4;
set_uint16(cp, (uint16_t)desc->n_intro_points);
cp += 2;
for (i=0; i < desc->n_intro_points; ++i) {
ipoint = (char*)desc->intro_points[i];
strcpy(cp, ipoint);
cp += strlen(ipoint)+1;
}
i = crypto_SHA_digest(*str_out, cp-*str_out, digest);
if (i<0) {
tor_free(*str_out);
return -1;
}
i = crypto_pk_private_sign(key, digest, CRYPTO_SHA1_DIGEST_LEN, cp);
if (i<0) {
tor_free(*str_out);
return -1;
}
cp += i;
assert(*len_out == (cp-*str_out));
return 0;
}
rend_service_descriptor_t *rend_parse_service_descriptor(
const char *str, int len)
{
rend_service_descriptor_t *result = NULL;
int keylen, asn1len, i;
const char *end, *cp, *eos;
char *signed_data=NULL;
char digest_expected[CRYPTO_SHA1_DIGEST_LEN];
result = tor_malloc_zero(sizeof(rend_service_descriptor_t));
cp = str;
end = str+len;
if (end-cp < 2) goto truncated;
asn1len = get_uint16(cp);
cp += 2;
if (end-cp < asn1len) goto truncated;
result->pk = crypto_pk_asn1_decode(cp, asn1len);
if (!result->pk) goto truncated;
cp += asn1len;
if (end-cp < 4) goto truncated;
result->timestamp = (time_t) get_uint32(cp);
cp += 4;
if (end-cp < 2) goto truncated;
result->n_intro_points = get_uint16(cp);
result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
cp += 2;
for (i=0;i<result->n_intro_points;++i) {
if (end-cp < 2) goto truncated;
eos = (const char *)memchr(cp,'\0',end-cp);
if (!eos) goto truncated;
result->intro_points[i] = tor_strdup(cp);
cp = eos+1;
}
keylen = crypto_pk_keysize(result->pk);
if (end-cp != keylen) goto truncated;
if (crypto_SHA_digest(str, cp-str, digest_expected)<0) {
log_fn(LOG_WARN, "Error computing SHA1 digest.");
goto error;
}
signed_data = tor_malloc(keylen+1);
i = crypto_pk_public_checksig(result->pk, (char*)cp, end-cp, signed_data);
if (i<0) {
log_fn(LOG_WARN, "Invalid signature on service descriptor");
goto error;
}
if (i != CRYPTO_SHA1_DIGEST_LEN ||
memcmp(signed_data, digest_expected, CRYPTO_SHA1_DIGEST_LEN)) {
log_fn(LOG_WARN, "Mismatched signature on service descriptor");
goto error;
}
tor_free(signed_data);
return result;
truncated:
log_fn(LOG_WARN, "Truncated service descriptor");
error:
tor_free(signed_data);
rend_service_descriptor_free(result);
return NULL;
}

View File

@ -804,6 +804,40 @@ test_dir_format()
test_eq(0, is_recommended_version("a", "")); test_eq(0, is_recommended_version("a", ""));
} }
void test_rend_fns()
{
rend_service_descriptor_t *d1, *d2;
char *encoded;
int len;
crypto_pk_env_t *pk1;
time_t now;
pk1 = crypto_new_pk_env(CRYPTO_PK_RSA);
test_assert(!crypto_pk_generate_key(pk1));
d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
d1->pk = pk1;
now = time(NULL);
d1->timestamp = now;
d1->n_intro_points = 3;
d1->intro_points = tor_malloc(sizeof(char*)*3);
d1->intro_points[0] = tor_strdup("tom");
d1->intro_points[1] = tor_strdup("crow");
d1->intro_points[2] = tor_strdup("joel");
test_assert(! rend_encode_service_descriptor(d1, pk1, &encoded, &len));
d2 = rend_parse_service_descriptor(encoded, len);
test_assert(d2);
test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
test_eq(d2->timestamp, now);
test_eq(d2->n_intro_points, 3);
test_streq(d2->intro_points[0], "tom");
test_streq(d2->intro_points[1], "crow");
test_streq(d2->intro_points[2], "joel");
rend_service_descriptor_free(d1);
rend_service_descriptor_free(d2);
}
int int
main(int c, char**v){ main(int c, char**v){
#if 0 #if 0
@ -830,6 +864,8 @@ main(int c, char**v){
puts("\n========================= Directory Formats ==============="); puts("\n========================= Directory Formats ===============");
// add_stream_log(LOG_DEBUG, NULL, stdout); // add_stream_log(LOG_DEBUG, NULL, stdout);
test_dir_format(); test_dir_format();
puts("\n========================= Rendezvous functionality ========");
test_rend_fns();
puts(""); puts("");
if (have_failed) if (have_failed)