浏览代码

Parse and generate service descriptors

svn:r1403
Nick Mathewson 21 年之前
父节点
当前提交
6ea61d5e0d
共有 8 个文件被更改,包括 212 次插入8 次删除
  1. 2 2
      doc/TODO
  2. 2 2
      doc/rend-spec.txt
  3. 1 0
      src/common/crypto.c
  4. 25 2
      src/common/util.h
  5. 2 2
      src/or/Makefile.am
  6. 14 0
      src/or/or.h
  7. 130 0
      src/or/rendcommon.c
  8. 36 0
      src/or/test.c

+ 2 - 2
doc/TODO

@@ -105,9 +105,9 @@ Rendezvous service:
               can be in.) [NM]
             o Add circuit metadata [NM] 3 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
-            - Code to generate and parse service descriptors [NM] 4 hours
+            o Code to generate and parse service descriptors [NM] 4 hours
         - Advertisement
             . Generate y.onion hostnames [NM] 1 hour
             - Code to do an HTTP connection over Tor from within Tor [RD]

+ 2 - 2
doc/rend-spec.txt

@@ -108,13 +108,13 @@ Tor Rendezvous Spec
 
          KL    Key length                            [2 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]
          Ipt   A list of NUL-terminated OR nicknames [variable]
          SIG   Signature of above fields             [KL octets]
 
    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
    directory servers don't need to know Bob's chosen introduction points.

+ 1 - 0
src/common/crypto.c

@@ -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)
 {
   assert(env && env->key);

+ 25 - 2
src/common/util.h

@@ -49,13 +49,36 @@ void tor_strlower(char *s);
  */
 #define get_uint16(cp) (*(uint16_t*)(cp))
 #define get_uint32(cp) (*(uint32_t*)(cp))
-#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_uint16(cp,v) do { *(uint16_t*)(cp) = (v); } while (0)
+#define set_uint32(cp,v) do { *(uint32_t*)(cp) = (v); } while (0)
 #else
+#if 1
 uint16_t get_uint16(char *cp);
 uint32_t get_uint32(char *cp);
 void set_uint16(char *cp, uint16_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
 
 typedef struct {

+ 2 - 2
src/or/Makefile.am

@@ -5,14 +5,14 @@ noinst_PROGRAMS = test
 bin_PROGRAMS = tor
 
 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 \
              rephist.c cpuworker.c main.c tor_main.c
 
 tor_LDADD = ../common/libor.a
 
 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 \
              rephist.c cpuworker.c main.c test.c
 

+ 14 - 0
src/or/or.h

@@ -972,6 +972,20 @@ void rep_hist_dump_stats(time_t now, int severity);
 /* length of 'y' portion of 'y.onion' URL. */
 #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
 
 /*

+ 130 - 0
src/or/rendcommon.c

@@ -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;
+}
+

+ 36 - 0
src/or/test.c

@@ -804,6 +804,40 @@ test_dir_format()
   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
 main(int c, char**v){
 #if 0
@@ -830,6 +864,8 @@ main(int c, char**v){
   puts("\n========================= Directory Formats ===============");
 //  add_stream_log(LOG_DEBUG, NULL, stdout);
   test_dir_format();
+  puts("\n========================= Rendezvous functionality ========");
+  test_rend_fns();
   puts("");
 
   if (have_failed)