[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r10640: added encoding and parsing of new ASCII-based (v2) rendezvou (in tor/branches/114-dist-storage: . src/or)



Author: kloesing
Date: 2007-06-17 11:40:27 -0400 (Sun, 17 Jun 2007)
New Revision: 10640

Modified:
   tor/branches/114-dist-storage/ChangeLog
   tor/branches/114-dist-storage/src/or/config.c
   tor/branches/114-dist-storage/src/or/connection_edge.c
   tor/branches/114-dist-storage/src/or/directory.c
   tor/branches/114-dist-storage/src/or/or.h
   tor/branches/114-dist-storage/src/or/rendclient.c
   tor/branches/114-dist-storage/src/or/rendcommon.c
   tor/branches/114-dist-storage/src/or/rendservice.c
   tor/branches/114-dist-storage/src/or/routerparse.c
Log:
added encoding and parsing of new ASCII-based (v2) rendezvous service descriptors

Modified: tor/branches/114-dist-storage/ChangeLog
===================================================================
--- tor/branches/114-dist-storage/ChangeLog	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/ChangeLog	2007-06-17 15:40:27 UTC (rev 10640)
@@ -1,3 +1,39 @@
+Changes in branch 114-dist-storage - 2007-??-??
+
+  o Completed features:
+    - Hidden service providers can set the config option
+      StoreV2HidServDescriptorsToDisk to store v2 descriptors to their local
+      data directory whenever they consider publishing a new service descriptor
+      (for testing purposes only).
+    - On startup, hidden service providers check whether they publish v2
+      service descriptors and if they have already created a secret_cookie and
+      a hostname2 file; if not, they create them.
+    - Hidden service providers can set the config option
+      PublishV2HidServDescriptors to publish v2 descriptors to the directory
+      (publishing is not implemented, yet).
+    - Hidden service clients can set the config option
+      LoadV2HidServDescriptorsFromDisk to load v2 descriptors from their local
+      data directory whenever they get a request for a v2 onion address.
+    - Hidden service clients handle SOCKS requests for v2 onion addresses by
+      trying to load v2 descriptors from the data directory, parse them, and
+      store them to the renddesc cache.
+    - Hidden service clients can set the config option
+      FetchV2HidServDescriptors to fetch v2 descriptors from the directory
+      (fetching is not implemented, yet).
+
+  o Planned features:
+    - Hidden service providers can set the config option HiddenServiceCookie
+      that overrides the random secret_cookie with one or more user defined
+      passphrases, or rather hashes of those.
+    - Hidden service directory nodes parse and store v2 service descriptors
+      from hidden service providers.
+    - Hidden service directory nodes serve v2 service descriptors to hidden
+      service clients.
+    - Hidden service clients try to fetch v2 service descriptors from directory
+      nodes (instead of loading them from the local data directory as it is
+      currently done).
+
+
 Changes in version 0.2.0.3-alpha - 2007-??-??
   o Minor features:
     - Create listener connections before we setuid to the configured User and

Modified: tor/branches/114-dist-storage/src/or/config.c
===================================================================
--- tor/branches/114-dist-storage/src/or/config.c	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/config.c	2007-06-17 15:40:27 UTC (rev 10640)
@@ -174,6 +174,7 @@
   VAR("FastFirstHopPK",      BOOL,     FastFirstHopPK,       "1"),
   VAR("FetchServerDescriptors",BOOL,   FetchServerDescriptors,"1"),
   VAR("FetchHidServDescriptors",BOOL,  FetchHidServDescriptors, "1"),
+  VAR("FetchV2HidServDescriptors",BOOL,FetchV2HidServDescriptors,"0"),
   VAR("FetchUselessDescriptors",BOOL,  FetchUselessDescriptors, "0"),
   VAR("Group",               STRING,   Group,                NULL),
   VAR("HardwareAccel",       BOOL,     HardwareAccel,        "0"),
@@ -191,6 +192,8 @@
   VAR("HttpsProxyAuthenticator",STRING,HttpsProxyAuthenticator,NULL),
   OBSOLETE("IgnoreVersion"),
   VAR("KeepalivePeriod",     INTERVAL, KeepalivePeriod,      "5 minutes"),
+  VAR("LoadV2HidServDescriptorsFromDisk",BOOL,
+                                       LoadV2HidServDescriptorsFromDisk,"0"),
   VAR("Log",                 LINELIST, Logs,                 NULL),
   OBSOLETE("LinkPadding"),
   OBSOLETE("LogLevel"),
@@ -221,6 +224,7 @@
   VAR("ProtocolWarnings",    BOOL,     ProtocolWarnings,     "0"),
   VAR("PublishServerDescriptor", CSV,  PublishServerDescriptor,"v1,v2"),
   VAR("PublishHidServDescriptors",BOOL,PublishHidServDescriptors, "1"),
+  VAR("PublishV2HidServDescriptors",BOOL,PublishV2HidServDescriptors,"0"),
   VAR("ReachableAddresses",  LINELIST, ReachableAddresses,   NULL),
   VAR("ReachableDirAddresses",LINELIST,ReachableDirAddresses,NULL),
   VAR("ReachableORAddresses",LINELIST, ReachableORAddresses, NULL),
@@ -252,6 +256,8 @@
   VAR("SocksPort",           UINT,     SocksPort,            "9050"),
   VAR("SocksTimeout",        INTERVAL, SocksTimeout,         "2 minutes"),
   OBSOLETE("StatusFetchPeriod"),
+  VAR("StoreV2HidServDescriptorsToDisk",BOOL,
+                                       StoreV2HidServDescriptorsToDisk,"0"),
   VAR("StrictEntryNodes",    BOOL,     StrictEntryNodes,     "0"),
   VAR("StrictExitNodes",     BOOL,     StrictExitNodes,      "0"),
   OBSOLETE("SysLog"),

Modified: tor/branches/114-dist-storage/src/or/connection_edge.c
===================================================================
--- tor/branches/114-dist-storage/src/or/connection_edge.c	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/connection_edge.c	2007-06-17 15:40:27 UTC (rev 10640)
@@ -1375,6 +1375,7 @@
     /* it's a hidden-service request */
     rend_cache_entry_t *entry;
     int r;
+    time_t now = time(NULL);
     tor_assert(!automap);
     if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
       /* if it's a resolve request, fail it right now, rather than
@@ -1407,16 +1408,30 @@
       connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
       return -1;
     }
+/** How long after we receive a hidden service descriptor do we consider
+ * it valid? */
+#define NUM_SECONDS_BEFORE_HS_REFETCH (60*15)
+    /* if necessary and configured, try to read descriptor from disk */
+    if (get_options()->LoadV2HidServDescriptorsFromDisk &&
+        (r==0 || (r>0 && now - entry->received >=
+                 NUM_SECONDS_BEFORE_HS_REFETCH))) {
+      if (rend_load_service_descriptor_from_disk(socks->address,
+                                                 time(NULL)) < 0) {
+        log_warn(LD_REND, "Loading of renddesc from disk failed for id %s",
+                 escaped_safe_str(socks->address));
+      } else {
+        log_debug(LD_REND,"Loading of renddesc from disk successful for id %s",
+                  escaped_safe_str(socks->address));
+        r = rend_cache_lookup_entry(socks->address, -1, &entry);
+      }
+    }
     if (r==0) {
       conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
       log_info(LD_REND, "Unknown descriptor %s. Fetching.",
                safe_str(conn->rend_query));
       rend_client_refetch_renddesc(conn->rend_query);
     } else { /* r > 0 */
-/** How long after we receive a hidden service descriptor do we consider
- * it valid? */
-#define NUM_SECONDS_BEFORE_HS_REFETCH (60*15)
-      if (time(NULL) - entry->received < NUM_SECONDS_BEFORE_HS_REFETCH) {
+      if (now - entry->received < NUM_SECONDS_BEFORE_HS_REFETCH) {
         conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
         log_info(LD_REND, "Descriptor is here and fresh enough. Great.");
         if (connection_ap_handshake_attach_circuit(conn) < 0) {
@@ -2582,31 +2597,27 @@
 hostname_type_t
 parse_extended_hostname(char *address)
 {
-    char *s;
-    char query[REND_SERVICE_ID_LEN+1];
+  char *s;
 
-    s = strrchr(address,'.');
-    if (!s)
-      return NORMAL_HOSTNAME; /* no dot, thus normal */
-    if (!strcmp(s+1,"exit")) {
-      *s = 0; /* nul-terminate it */
-      return EXIT_HOSTNAME; /* .exit */
-    }
-    if (strcmp(s+1,"onion"))
-      return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
+  s = strrchr(address,'.');
+  if (!s)
+    return NORMAL_HOSTNAME; /* no dot, thus normal */
+  if (!strcmp(s+1,"exit")) {
+    *s = 0; /* nul-terminate it */
+    return EXIT_HOSTNAME; /* .exit */
+  }
+  if (strcmp(s+1,"onion"))
+    return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
 
-    /* so it is .onion */
-    *s = 0; /* nul-terminate it */
-    if (strlcpy(query, address, REND_SERVICE_ID_LEN+1) >=
-        REND_SERVICE_ID_LEN+1)
-      goto failed;
-    if (rend_valid_service_id(query)) {
-      return ONION_HOSTNAME; /* success */
-    }
-failed:
-    /* otherwise, return to previous state and return 0 */
-    *s = '.';
-    return BAD_HOSTNAME;
+  /* so it is .onion */
+  *s = 0; /* nul-terminate it */
+  if (rend_valid_service_id(address) || rend_valid_v2_service_id(address)) {
+    return ONION_HOSTNAME; /* success */
+  }
+
+  /* otherwise, return to previous state and return 0 */
+  *s = '.';
+  return BAD_HOSTNAME;
 }
 
 /** Check if the address is of the form "y.noconnect"

Modified: tor/branches/114-dist-storage/src/or/directory.c
===================================================================
--- tor/branches/114-dist-storage/src/or/directory.c	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/directory.c	2007-06-17 15:40:27 UTC (rev 10640)
@@ -1367,7 +1367,11 @@
              (int)body_len, status_code, escaped(reason));
     switch (status_code) {
       case 200:
-        if (rend_cache_store(body, body_len, 0) < 0) {
+        /* TODO when implementing storing/fetching v2 descriptors, replace NULL
+         * by secret_cookie that was given to us in the onion address when the
+         * client requested the hidden service (need to memorize that
+         * somewhere). This way it won't work!! */
+        if (rend_cache_store(body, body_len, 0, 1, NULL) < 0) {
           log_warn(LD_REND,"Failed to store rendezvous descriptor.");
           /* alice's ap_stream will notice when connection_mark_for_close
            * cleans it up */
@@ -2055,7 +2059,7 @@
   if (options->HSAuthoritativeDir &&
       !strcmpstart(url,"/tor/rendezvous/publish")) {
     /* rendezvous descriptor post */
-    if (rend_cache_store(body, body_len, 1) < 0) {
+    if (rend_cache_store(body, body_len, 1, 1, NULL) < 0) {
 //      char tmp[1024*2+1];
       log_fn(LOG_PROTOCOL_WARN, LD_DIRSERV,
              "Rejected rend descriptor (length %d) from %s.",

Modified: tor/branches/114-dist-storage/src/or/or.h
===================================================================
--- tor/branches/114-dist-storage/src/or/or.h	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/or.h	2007-06-17 15:40:27 UTC (rev 10640)
@@ -1870,8 +1870,14 @@
   authority_type_t _PublishServerDescriptor;
   /** Boolean: do we publish hidden service descriptors to the HS auths? */
   int PublishHidServDescriptors;
+  int PublishV2HidServDescriptors; /**< and v2 hidden service descriptors? */
+  /** do we store v2 hidden service descriptors to disk? */
+  int StoreV2HidServDescriptorsToDisk;
+  /** do we first try to read v2 hidden service descriptors from disk? */
+  int LoadV2HidServDescriptorsFromDisk;
   int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */
   int FetchHidServDescriptors; /** and hidden service descriptors? */
+  int FetchV2HidServDescriptors; /** and v2 hidden service descriptors? */
   int FetchUselessDescriptors; /**< Do we fetch non-running descriptors too? */
   int AllDirActionsPrivate; /**< Should every directory action be sent
                              * through a Tor circuit? */
@@ -3043,13 +3049,14 @@
 
 int rend_client_send_introduction(origin_circuit_t *introcirc,
                                   origin_circuit_t *rendcirc);
+int rend_load_service_descriptor_from_disk(const char *service_id, time_t now);
 
 /********************************* rendcommon.c ***************************/
 
 /** Information used to connect to a hidden service. */
 typedef struct rend_service_descriptor_t {
   crypto_pk_env_t *pk; /**< This service's public key. */
-  int version; /**< 0 or 1. */
+  int version; /**< 0 to 2. */
   time_t timestamp; /**< Time when the descriptor was generated. */
   uint16_t protocols; /**< Bitmask: which rendezvous protocols are supported?
                        * (We allow bits '0', '1', and '2' to be set.) */
@@ -3063,6 +3070,27 @@
    * from this array if introduction attempts fail.  If this array is present,
    * its elements correspond to the elements of intro_points. */
   extend_info_t **intro_point_extend_info;
+  /** Descriptor identifiers (current and next) for distributed storage. */
+  char desc_id[2][20];
+  /** Number of seconds that the current desc_id will be valid. */
+  int seconds_valid;
+  /** Permanent part of the computation of desc_id that is derived from pk
+   * or passed by a client as part of the onion address. */
+  char permanent_id[10];
+  /** Changing part (current and next) of the computation of desc_id that is
+   * computed from time_period and secret_cookie. */
+  char secret_id_part[2][20];
+  /** Derived value of current and next time period. */
+  char time_period[2][4];
+  /** AES-encrypted list of introduction points. */
+  char *intro_points_encrypted;
+  /** Size of encrypted string. */
+  int intro_points_encrypted_size;
+  /** Secret cookie for generating secret_id_part and {en|de}crypting the
+   * introduction points. Is randomly generated by the hidden service provider
+   * on first configuration of the service and passed by a client as part of
+   * the onion address; storing nodes do not know this and set it to NULL. */
+  char secret_cookie[16];
 } rend_service_descriptor_t;
 
 int rend_cmp_service_ids(const char *one, const char *two);
@@ -3092,12 +3120,21 @@
 void rend_cache_clean(void);
 void rend_cache_free_all(void);
 int rend_valid_service_id(const char *query);
+int rend_valid_v2_service_id(const char *query);
 int rend_cache_lookup_desc(const char *query, int version, const char **desc,
                            size_t *desc_len);
 int rend_cache_lookup_entry(const char *query, int version,
                             rend_cache_entry_t **entry_out);
-int rend_cache_store(const char *desc, size_t desc_len, int published);
+int rend_cache_store(const char *desc, size_t desc_len, int published,
+                     int version, const char *secret_cookie);
 int rend_cache_size(void);
+int rend_encode_v2_descriptor(char *current_desc, char *next_desc,
+                              rend_service_descriptor_t *desc,
+                              time_t now, //crypto_pk_env_t *key,
+                              const char *secret_cookie);//,
+                              //char *descriptor_id_base32);
+int rend_compute_desc_id(char *desc_id, const char *permanent_id,
+                         const char *secret_cookie, time_t now);
 
 /********************************* rendservice.c ***************************/
 
@@ -3449,6 +3486,10 @@
 
 authority_cert_t *authority_cert_parse_from_string(const char *s,
                                                    const char **end_of_string);
+int rend_parse_v2_service_descriptor(rend_service_descriptor_t *result,
+                                     const char *str);
+int rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
+                                 const char *secret_cookie);
 
 #endif
 

Modified: tor/branches/114-dist-storage/src/or/rendclient.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendclient.c	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/rendclient.c	2007-06-17 15:40:27 UTC (rev 10640)
@@ -247,6 +247,57 @@
   return 0;
 }
 
+/* Loads the rendezvous service descriptor for <b>address</b> from a file with
+ * file name "<corresponding descriptor id>.onion" from the data directory,
+ * parses it and stores it to the descritor cache, if it is valid. Returns 0 if
+ * successful, -1 otherwise. */
+int
+rend_load_service_descriptor_from_disk(const char *address, time_t now)
+{
+
+  char *s = NULL; /* string read from file */
+  char *fname; /* file name */
+  int flen; /* length of file name */
+  char service_id[10]; /* service id part of address in binary format */
+  char secret_cookie[15]; /* cookie part of address in binary format */
+  char descriptor_id_digest[20]; /* computed descriptor id in binary format */
+  char descriptor_id_base32[32+1]; /* base32-formatted descriptor id */
+
+  /* address is formatted like "oo4qinerb64ybxkd.xvqwspfbixjhz2i2qwixb2ej" */
+
+  /* extract service id */
+  base32_decode(service_id, 10, address, 16);
+  
+  /* extract cookie */
+  base32_decode(secret_cookie, 15, address+17, 24);
+
+  /* compute current descriptor id */
+  rend_compute_desc_id(descriptor_id_digest, service_id, secret_cookie, now);
+
+  /* encode base 32 representation of descriptor id */
+  base32_encode(descriptor_id_base32, 32+1, descriptor_id_digest, 20);
+
+  /* construct file name */
+  flen = strlen(get_options()->DataDirectory) + 40;
+  fname = tor_malloc(flen);
+  tor_snprintf(fname, flen, "%s"PATH_SEPARATOR"%s.rsd",
+               get_options()->DataDirectory, descriptor_id_base32);
+
+  /* read string from file */
+  if (!(s = read_file_to_str(fname, 0, NULL))) {
+    log_warn(LD_FS, "Unable to read descriptor \"%s\"", fname);
+    return -1;
+  }
+
+  /* parse and store descriptor */
+  if (rend_cache_store(s, strlen(s), 0, 2, secret_cookie) < 0) {
+    log_warn(LD_FS, "Could not add descriptor to the cache");
+    return -1;
+  }
+
+  return 0;
+}
+
 /** If we are not currently fetching a rendezvous service descriptor
  * for the service ID <b>query</b>, start a directory connection to fetch a
  * new one.
@@ -254,8 +305,12 @@
 void
 rend_client_refetch_renddesc(const char *query)
 {
-  if (!get_options()->FetchHidServDescriptors)
+  if (!get_options()->FetchHidServDescriptors &&
+      !get_options()->FetchV2HidServDescriptors)
     return;
+
+  /* TODO fetching v2 hidden service descriptors not implemented yet! */
+
   if (connection_get_by_type_state_rendquery(CONN_TYPE_DIR, 0, query)) {
     log_info(LD_REND,"Would fetch a new renddesc here (for %s), but one is "
              "already in progress.", escaped_safe_str(query));

Modified: tor/branches/114-dist-storage/src/or/rendcommon.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendcommon.c	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/rendcommon.c	2007-06-17 15:40:27 UTC (rev 10640)
@@ -43,6 +43,292 @@
   tor_free(desc);
 }
 
+#define TIME_PERIOD_LENGTH 600
+  
+/* Fill the fields of <b>desc</b> so that it a valid v2 descriptor can be
+ * encoded from it. Use the current time <b>now</b> and the
+ * <b>secret_cookie</b>. Returns 0 on success, -1 otherwise. */
+static int
+rend_compute_v2_descriptor_fields(rend_service_descriptor_t *desc,
+                                  time_t now, const char *secret_cookie)
+{
+
+  char permanent_id_temp[DIGEST_LEN];
+  double time_period_part1;
+  double time_period_part2;
+  uint64_t permanent_id_value = 0;
+  uint32_t time_period;
+  int i;
+  crypto_digest_env_t *digest;
+
+  /* obtain permanent-id from public key */
+  crypto_pk_get_digest(desc->pk, permanent_id_temp);
+  memcpy(desc->permanent_id, permanent_id_temp, 10);
+
+  /* copy secret_cookie */
+  memcpy(desc->secret_cookie, secret_cookie, 15);
+  desc->secret_cookie[15] = 0;
+
+  /* calculate current time-period */
+  time_period_part1 = ((double) now) / ((double) TIME_PERIOD_LENGTH);
+  permanent_id_value=((((uint64_t) desc->permanent_id[0]) + 256) % 256) << 32;
+  permanent_id_value+=((((uint64_t) desc->permanent_id[1]) + 256) % 256) << 24;
+  permanent_id_value+=((((uint64_t) desc->permanent_id[2]) + 256) % 256) << 16;
+  permanent_id_value+=((((uint64_t) desc->permanent_id[3]) + 256) % 256) << 8;
+  permanent_id_value+=(((uint64_t) desc->permanent_id[4]) + 256) % 256;
+  time_period_part2 = ((double) permanent_id_value) /
+                              ((double) (((uint64_t) 1) << 40));
+  time_period = (uint32_t) (time_period_part1 + time_period_part2);
+  desc->seconds_valid = TIME_PERIOD_LENGTH - (uint32_t) (((time_period_part1
+          + time_period_part2) - (double) time_period) * TIME_PERIOD_LENGTH);
+
+  /* Compute descriptor ids for the current and the next time period */
+  for (i = 0; i < 2; i++) {
+
+    /* time-period */
+    desc->time_period[i][0] = (char) ((time_period+i) >> 24);
+    desc->time_period[i][1] = (char) ((time_period+i) >> 16);
+    desc->time_period[i][2] = (char) ((time_period+i) >> 8);
+    desc->time_period[i][3] = (char) (time_period+i);
+
+    /* secret-id-part = h(time-period + cookie) */
+    digest = crypto_new_digest_env();
+    crypto_digest_add_bytes(digest, desc->time_period[i], 4);
+    crypto_digest_add_bytes(digest, desc->secret_cookie, 16);
+    crypto_digest_get_digest(digest, desc->secret_id_part[i], 20);
+    crypto_free_digest_env(digest);
+
+    /* descriptor id */
+    digest = crypto_new_digest_env();
+    crypto_digest_add_bytes(digest, desc->permanent_id, 10);
+    crypto_digest_add_bytes(digest, desc->secret_id_part[i], 20);
+    crypto_digest_get_digest(digest, desc->desc_id[i], 20);
+    crypto_free_digest_env(digest);
+  }
+
+  return 0;
+}
+
+/* Compute the <b>desc_id</b> for a given <b>service_id</b> and
+ * <b>secret_cookie</b> at time <b>now</b>. */
+int
+rend_compute_desc_id(char *desc_id, const char *service_id,
+                     const char *secret_cookie, time_t now)
+{
+  uint64_t permanent_id_value = 0;
+  double time_period_part1;
+  double time_period_part2;
+  char time_period_bytes[4];
+  uint32_t time_period;
+  crypto_digest_env_t *digest;
+  char secret_id_part_digest[20];
+  char secret_cookie_16[16];
+
+  /* time-period */
+  time_period_part1 = ((double) now) / ((double) TIME_PERIOD_LENGTH);
+  permanent_id_value=((((uint64_t) service_id[0]) + 256) % 256) << 32;
+  permanent_id_value+=((((uint64_t) service_id[1]) + 256) % 256) << 24;
+  permanent_id_value+=((((uint64_t) service_id[2]) + 256) % 256) << 16;
+  permanent_id_value+=((((uint64_t) service_id[3]) + 256) % 256) << 8;
+  permanent_id_value+=(((uint64_t) service_id[4]) + 256) % 256;
+  time_period_part2 = ((double) permanent_id_value) /
+                              ((double) (((uint64_t) 1) << 40));
+  time_period = (uint32_t) (time_period_part1 + time_period_part2);
+  time_period_bytes[0] = (char) (time_period >> 24);
+  time_period_bytes[1] = (char) (time_period >> 16);
+  time_period_bytes[2] = (char) (time_period >> 8);
+  time_period_bytes[3] = (char) time_period;
+
+  /* secret-id-part = h(time-period + cookie) */
+  digest = crypto_new_digest_env();
+  crypto_digest_add_bytes(digest, time_period_bytes, 4);
+  memcpy(secret_cookie_16, secret_cookie, 15);
+  secret_cookie_16[15] = 0;
+  crypto_digest_add_bytes(digest, secret_cookie_16, 16);
+  crypto_digest_get_digest(digest, secret_id_part_digest, 20);
+  crypto_free_digest_env(digest);
+
+  /* descriptor id */
+  digest = crypto_new_digest_env();
+  crypto_digest_add_bytes(digest, service_id, 10);
+  crypto_digest_add_bytes(digest, secret_id_part_digest, 20);
+  crypto_digest_get_digest(digest, desc_id, 20);
+  crypto_free_digest_env(digest);
+
+  return 0;
+}
+
+/* Encodes the current (<b>current_desc</b>) and next descriptor (<b>next_desc</b>) from
+ * <b>desc</b>. Returns 0 on success, -1 otherwise. */
+int
+rend_encode_v2_descriptor(char *current_desc, char *next_desc,
+                          rend_service_descriptor_t *desc, time_t now,
+                          const char *secret_cookie)
+{
+
+  char *pkey; /* public key, PEM-encoded. */
+  size_t pkeylen; /* public key length */
+  char published[ISO_TIME_LEN+1]; /* timestamp */
+  size_t iposlen; /* max length of unencrypted introduction points */
+  char *ipos; /* unencrypted introduction points */
+  int ipowritten; /* actual length of unencrypted introduction points */
+  char secret_id_part_base32[32+1];
+  char descriptor_id_base32[32+1];
+  int i;
+  char *ipos_enc;
+  int enclen;
+  char *ipos_encrypted_base64;
+  int result = 0;
+  size_t written = 0;
+  char desc_digest[DIGEST_LEN];
+  char *buf;
+
+  /* prepare descriptor */
+  rend_compute_v2_descriptor_fields(desc, now, secret_cookie);
+
+  /* PEM-encode the public key */
+  if (crypto_pk_write_public_key_to_string(desc->pk, &pkey, &pkeylen) < 0) {
+    log_warn(LD_DIR, "write public key failed!");
+    return -1;
+  }
+
+  /* encode timestamp */
+  format_iso_time(published, desc->timestamp);
+
+  /* assemble unencrypted list of introduction points */
+  iposlen = desc->n_intro_points * 700;
+  ipos = tor_malloc_zero(iposlen);
+  ipowritten = 0;
+  for (i=0; i < desc->n_intro_points; ++i) {
+    char id_base32[32+1];
+    char *okey; /* onion key, PEM-encoded. */
+    size_t okeylen;
+    char *skey; /* service key, PEM_encoded. */
+    size_t skeylen;
+    int res;
+
+    /* obtain extend info with introduction point details */
+    extend_info_t *info = desc->intro_point_extend_info[i];
+
+    /* encode introduction point ID */
+    base32_encode(id_base32, 32+1, info->identity_digest, DIGEST_LEN);
+
+    /* encode onion key */
+    if (crypto_pk_write_public_key_to_string(info->onion_key, &okey,
+                                             &okeylen) < 0) {
+      log_warn(LD_DIR, "write onion key failed");
+      continue;
+    }
+    
+    /* encode service key; TODO replace Bob's public key by newly generated
+     * service key */
+    if (crypto_pk_write_public_key_to_string(desc->pk, &skey, &skeylen) < 0) {
+      log_warn(LD_DIR, "write service key failed");
+      continue;
+    }
+    
+    /* assemble everything for this introduction point */
+    res = tor_snprintf(ipos+ipowritten, iposlen-ipowritten,
+                       "introduction-point %s\n"
+                       "ip-address %u.%u.%u.%u\n"
+                       "onion-port %d\n"
+                       "onion-key\n%s"
+                       "service-key\n%s",
+      id_base32,
+      (info->addr >> 24) % 256,
+      (info->addr >> 16) % 256,
+      (info->addr >> 8) % 256,
+      info->addr % 256,
+      info->port,
+      okey,
+      skey);
+    if (res < 0) {
+      log_warn(LD_BUG, "not enough space for writing ipo");
+      return -1;
+    }
+    
+    /* update total number of written bytes for unencrypted intro points */
+    ipowritten += res;
+  }
+  
+  /* finalize unencrypted introduction points */
+  ipos[ipowritten++] = '\n';
+  ipos[ipowritten++] = 0;
+
+  /* encrypt introduction points */
+  ipos_enc = tor_malloc_zero(ipowritten + 32);
+  enclen = crypto_cipher_encrypt_cbc(desc->secret_cookie, ipos_enc,
+                                     ipos, ipowritten);
+
+  /* free memory for unencrypted introduction points */
+  tor_free(ipos);
+
+  /* base64-encode introduction points */
+  ipos_encrypted_base64 = tor_malloc_zero(ipowritten * 2);
+  if (base64_encode(ipos_encrypted_base64, ipowritten * 2, ipos_enc,
+                    enclen) < 0) {
+    log_warn(LD_DIR, "could not encode ipos to base64");
+    return -1;
+  }
+
+  /* encode both, the current and the next descriptor */
+  for (i = 0; i < 2; i++) {
+    buf = (i == 0 ? current_desc : next_desc);
+    base32_encode(secret_id_part_base32, 32+1, desc->secret_id_part[i], 20);
+    base32_encode(descriptor_id_base32, 32+1, desc->desc_id[i], 20);
+
+    /* complete descriptor; TODO make 4000 a little more dynamic */
+    result = tor_snprintf(buf, 4000,
+           "rendezvous-service-descriptor %s\n"
+           "version 2\n"
+           "permanent-key\n%s"
+           "secret-id-part %s\n"
+           "publication-time %s\n"
+           "protocol-versions %d\n"
+           "introduction-points\n-----BEGIN AES ENCRYPTED MESSAGE-----\n%s"
+           "-----END AES ENCRYPTED MESSAGE-----\n",
+      descriptor_id_base32,
+      pkey,
+      secret_id_part_base32,
+      published,
+      desc->protocols,
+      ipos_encrypted_base64);
+    if (result < 0) {
+      log_warn(LD_BUG, "descriptor ran out of room!");
+      return -1;
+    }
+    written = result;
+    
+    /* add signature */
+    strlcpy(buf + written, "signature\n", 4000 - written);
+    written += strlen(buf + written);
+    buf[written] = '\0';
+    if (crypto_digest(desc_digest, buf, written) < 0) {
+      log_warn(LD_BUG, "could not create digest");
+      return -1;
+    }
+    if (router_append_dirobj_signature(buf+written,4000-written,
+                                       desc_digest,desc->pk) < 0) {
+      log_warn(LD_BUG, "Couldn't sign desc");
+      return -1;
+    }
+    written += strlen(buf+written);
+    if (written+2 > 4000) {
+      log_warn(LD_BUG, "could not finish desc");
+      return -1;
+    }
+    buf[written++] = '\n';
+    buf[written++] = 0;
+
+  }
+  
+  /* free memory */
+  tor_free(pkey);
+
+  /* return descriptor size (both descriptors have the same size) */
+  return written;
+}
+
 /** Encode a service descriptor for <b>desc</b>, and sign it with
  * <b>key</b>. Store the descriptor in *<b>str_out</b>, and set
  * *<b>len_out</b> to its length.
@@ -307,6 +593,20 @@
   return 1;
 }
 
+/** Return true iff <b>query</b> is a syntactically valid v2 service ID */
+int
+rend_valid_v2_service_id(const char *query)
+{
+  if (strlen(query) != 16 + 1 + 24)
+    return 0;
+  if (strspn(query, BASE32_CHARS) != 40
+      && strspn(query+16, ".") != 1
+      && strspn(query+17, BASE32_CHARS) != 24)
+    return 0;
+
+  return 1;
+}
+
 /** If we have a cached rend_cache_entry_t for the service ID <b>query</b>,
  * set *<b>e</b> to that entry and return 1.  Else return 0.  If
  * <b>version</b> is nonnegative, only return an entry in that descriptor
@@ -318,6 +618,19 @@
 {
   char key[REND_SERVICE_ID_LEN+2]; /* 1<query>\0   or  0<query>\0 */
   tor_assert(rend_cache);
+  /* if request is v2 formatted, remove secret_cookie part */
+  if (rend_valid_v2_service_id(query)) {
+    char query_without_cookie[REND_SERVICE_ID_LEN+1];
+    *e = NULL;
+    strncpy(query_without_cookie, query, REND_SERVICE_ID_LEN);
+    tor_snprintf(key, sizeof(key), "1%s", query_without_cookie);
+    *e = strmap_get_lc(rend_cache, key);
+    if (!*e)
+      return 0;
+    return 1;
+  }
+
+  /* no v2 request? look for v0 or v1 renddesc */
   if (!rend_valid_service_id(query))
     return -1;
   *e = NULL;
@@ -362,9 +675,13 @@
  * it's the same or older than one we've already got; return 1 if
  * it's novel. The published flag tells us if we store the descriptor
  * in our role as directory (1) or if we cache it as client (0).
+ * The version can be either >= 2 for v2 descriptors or <= 1 for v0/v1
+ * descriptors. If published is 0, secret_cookie needs to contain a key,
+ * otherwise it is NULL.
  */
 int
-rend_cache_store(const char *desc, size_t desc_len, int published)
+rend_cache_store(const char *desc, size_t desc_len, int published, int version,
+                 const char *secret_cookie)
 {
   rend_cache_entry_t *e;
   rend_service_descriptor_t *parsed;
@@ -373,10 +690,23 @@
   time_t now;
   or_options_t *options = get_options();
   tor_assert(rend_cache);
-  parsed = rend_parse_service_descriptor(desc,desc_len);
-  if (!parsed) {
-    log_warn(LD_PROTOCOL,"Couldn't parse service descriptor.");
-    return -1;
+  if (version >= 2) {
+    parsed = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+    if (rend_parse_v2_service_descriptor(parsed, desc) < 0) {
+      log_warn(LD_FS, "Could not parse descriptor");
+      return -1;
+    }
+    if (!published &&
+        rend_decrypt_introduction_points(parsed, secret_cookie) < 0) {
+      log_warn(LD_PROTOCOL,"Couldn't decrypt introduction points.");
+      return -1;
+    }
+  } else {
+    parsed = rend_parse_service_descriptor(desc,desc_len);
+    if (!parsed) {
+      log_warn(LD_PROTOCOL,"Couldn't parse service descriptor.");
+      return -1;
+    }
   }
   if (rend_get_service_id(parsed->pk, query)<0) {
     log_warn(LD_BUG,"Couldn't compute service ID.");

Modified: tor/branches/114-dist-storage/src/or/rendservice.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendservice.c	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/rendservice.c	2007-06-17 15:40:27 UTC (rev 10640)
@@ -57,6 +57,7 @@
   rend_service_descriptor_t *desc;
   time_t desc_is_dirty;
   time_t next_upload_time;
+  char *secret_cookie;
 } rend_service_t;
 
 /** A list of rend_service_t's for services run on this OP.
@@ -320,6 +321,30 @@
   }
 }
 
+/* Write the v2 onion address of <b>service</b> to <b>hostname2</b>. */
+static int
+rend_get_hostname2(rend_service_t *service, char *hostname2)
+{
+
+  char permanent_id_base32[16+1];
+  char cookie_base32[24+1];
+
+  /* base32-encode service id */
+  base32_encode(permanent_id_base32, 16+1, service->pk_digest, 10);
+
+  /* base32-encode cookie */
+  base32_encode(cookie_base32, 24+1, service->secret_cookie, 15);
+
+  /* assemlbe onion address */  
+  if (tor_snprintf(hostname2, 50, "%s.%s.onion\n", permanent_id_base32,
+                   cookie_base32) < 0) {
+    log_warn(LD_BUG, "could not encode hostname");
+    return -1;
+  }
+
+  return 0;
+}
+
 /** Load and/or generate private keys for all hidden services.  Return 0 on
  * success, -1 on failure.
  */
@@ -332,6 +357,10 @@
   char buf[128];
 
   for (i=0; i < smartlist_len(rend_service_list); ++i) {
+
+    char hostname2[50];
+    char fname2[512];
+
     s = smartlist_get(rend_service_list,i);
     if (s->private_key)
       continue;
@@ -353,7 +382,23 @@
     s->private_key = init_key_from_file(fname, 1, LOG_ERR);
     if (!s->private_key)
       return -1;
-
+    /* Try to read secret_cookie file; if that fails, create new file */
+    if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
+        strlcat(fname,PATH_SEPARATOR"secret_cookie",sizeof(fname))
+                                                  >= sizeof(fname)) {
+      log_warn(LD_CONFIG,"Directory name too long to store secret_cookie file:"
+               " \"%s\".", s->directory);
+      return -1;
+    }
+    if (!(s->secret_cookie = read_file_to_str(fname, 0, NULL))) {
+      s->secret_cookie = tor_malloc_zero(16);
+      crypto_rand(s->secret_cookie, 16);
+      if (write_str_to_file(fname, s->secret_cookie, 0) < 0) {
+        log_warn(LD_CONFIG, "Could not write secret_cookie file:"
+               " \"%s\".", s->directory);
+        return -1;
+      }
+    }
     /* Create service file */
     if (rend_get_service_id(s->private_key, s->service_id)<0) {
       log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
@@ -373,6 +418,18 @@
     tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
     if (write_str_to_file(fname,buf,0)<0)
       return -1;
+
+    /* write hostname2 file */
+    rend_get_hostname2(s, hostname2);
+    if (strlcpy(fname2,s->directory,sizeof(fname2)) >= sizeof(fname2) ||
+        strlcat(fname2,PATH_SEPARATOR"hostname2",sizeof(fname2))
+                                                  >= sizeof(fname2)) {
+      log_warn(LD_CONFIG, "Directory name too long to store hostname2 file:"
+               " \"%s\".", s->directory);
+      return -1;
+    }
+    if (write_str_to_file(fname2, hostname2, 0) < 0)
+      return -1;
   }
   return 0;
 }
@@ -1068,6 +1125,62 @@
   smartlist_free(exclude_routers);
 }
 
+/* Stores the current and possibly next rendezvous service descriptor for
+ * <b>service</b> at time <b>now</b> to one or two files with file name
+ * "<corresponding descriptor id>.onion" to the data directory. Returns 0 if
+ * successful, -1 otherwise. */
+static int
+rend_store_service_descriptor_to_disk(rend_service_t *service, time_t now)
+{
+
+  char *current_desc;
+  char *next_desc;
+  size_t flen; // len of file name
+  char *fname; // file name
+  char descriptor_id_base32[32+1];
+  int i, n = 1;
+
+  /* TODO make 4000 configurable */
+  current_desc = tor_malloc_zero(4000);
+  next_desc = tor_malloc_zero(4000);
+
+  /* TODO might be better to prevent this from being executed multiple times */
+  rend_service_update_descriptor(service);
+
+  /* encode descriptor */
+  if (rend_encode_v2_descriptor(current_desc, next_desc, service->desc, now,
+                                         service->secret_cookie) < 0) {
+    log_warn(LD_DIR, "could not encode descriptor");
+    return -1;
+  }
+
+  /* write to file */
+  flen = strlen(get_options()->DataDirectory) + 40;
+  fname = tor_malloc(flen);
+
+  /* write one or two descriptors to disk? TODO make 6 configurable */
+  if (service->desc->seconds_valid < 6) n = 2;
+
+  for (i = 0; i < 2; i++) {
+  	
+  	/* base32-encode descriptor id */
+    base32_encode(descriptor_id_base32, 32+1, service->desc->desc_id[i], 20);
+    
+    /* determine file name */
+    tor_snprintf(fname, flen, "%s"PATH_SEPARATOR"%s.rsd",
+                 get_options()->DataDirectory, descriptor_id_base32);
+                 
+    /* write string to file */
+    write_str_to_file(fname,(i == 0 ? current_desc : next_desc),0);
+  }
+
+  tor_free(current_desc);
+  tor_free(next_desc);
+  tor_free(fname);
+
+  return 0;
+}
+
 /** Regenerate and upload rendezvous service descriptors for all
  * services, if necessary. If the descriptor has been dirty enough
  * for long enough, definitely upload; else only upload when the
@@ -1083,7 +1196,9 @@
   rend_service_t *service;
   int rendpostperiod = get_options()->RendPostPeriod;
 
-  if (!get_options()->PublishHidServDescriptors)
+  if (!get_options()->PublishHidServDescriptors &&
+      !get_options()->PublishV2HidServDescriptors &&
+      !get_options()->StoreV2HidServDescriptorsToDisk)
     return;
 
   for (i=0; i < smartlist_len(rend_service_list); ++i) {
@@ -1098,7 +1213,18 @@
       /* if it's time, or if the directory servers have a wrong service
        * descriptor and ours has been stable for 30 seconds, upload a
        * new one of each format. */
-      upload_service_descriptor(service, 0);
+      if (get_options()->StoreV2HidServDescriptorsToDisk) {
+        if (rend_store_service_descriptor_to_disk(service, now) < 0) {
+          log_warn(LD_DIR, "could not store descriptor to disk");
+        }
+        log_warn(LD_DIR, "stored descriptor to disk");
+      }
+      if (get_options()->PublishHidServDescriptors) {
+        upload_service_descriptor(service, 0);
+      }
+
+      /* TODO publishing v2 hidden service descriptors not implemented yet! */
+
       service->next_upload_time = now + rendpostperiod;
     }
   }

Modified: tor/branches/114-dist-storage/src/or/routerparse.c
===================================================================
--- tor/branches/114-dist-storage/src/or/routerparse.c	2007-06-17 15:10:51 UTC (rev 10639)
+++ tor/branches/114-dist-storage/src/or/routerparse.c	2007-06-17 15:40:27 UTC (rev 10640)
@@ -76,6 +76,21 @@
   K_VOTE_DIGEST,
   K_CONSENSUS_DIGEST,
 
+  R_RENDEZVOUS_SERVICE_DESCRIPTOR,
+  R_VERSION,
+  R_PERMANENT_KEY,
+  R_SECRET_ID_PART,
+  R_PUBLICATION_TIME,
+  R_PROTOCOL_VERSIONS,
+  R_INTRODUCTION_POINTS,
+  R_SIGNATURE,
+
+  R_IPO_IDENTIFIER,
+  R_IPO_IP_ADDRESS,
+  R_IPO_ONION_PORT,
+  R_IPO_ONION_KEY,
+  R_IPO_SERVICE_KEY,
+
   _UNRECOGNIZED,
   _ERR,
   _EOF,
@@ -287,6 +302,31 @@
   END_OF_TABLE
 };
 
+/** List of tokens allowable in rendezvous service derscriptors */
+static token_rule_t desc_token_table[] = {
+  T1("rendezvous-service-descriptor", R_RENDEZVOUS_SERVICE_DESCRIPTOR, EQ(1), \
+     NO_OBJ),
+  T1("version", R_VERSION, EQ(1), NO_OBJ),
+  T1("permanent-key", R_PERMANENT_KEY, NO_ARGS, NEED_KEY_1024),
+  T1("secret-id-part", R_SECRET_ID_PART, EQ(1), NO_OBJ),
+  T1("publication-time", R_PUBLICATION_TIME, CONCAT_ARGS, NO_OBJ),
+  T1("protocol-versions", R_PROTOCOL_VERSIONS, EQ(1), NO_OBJ),
+  T1("introduction-points", R_INTRODUCTION_POINTS, NO_ARGS, NEED_OBJ),
+  T1("signature", R_SIGNATURE, NO_ARGS, NEED_OBJ),
+  END_OF_TABLE
+};
+
+/** List of tokens allowed in the (encrypted) list of introduction points of
+ * rendezvous service descriptors */
+static token_rule_t ipo_token_table[] = {
+  T1("introduction-point", R_IPO_IDENTIFIER, EQ(1), NO_OBJ),
+  T1("ip-address", R_IPO_IP_ADDRESS, EQ(1), NO_OBJ),
+  T1("onion-port", R_IPO_ONION_PORT, EQ(1), NO_OBJ),
+  T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024),
+  T1("service-key", R_IPO_SERVICE_KEY, NO_ARGS, NEED_KEY_1024),
+  END_OF_TABLE
+};
+
 static token_rule_t networkstatus_vote_token_table[] = {
   T1("network-status-version", K_NETWORK_STATUS_VERSION,
                                                    GE(1),       NO_OBJ ),
@@ -2802,3 +2842,246 @@
     smartlist_uniq(versions, _compare_tor_version_str_ptr, NULL);
 }
 
+
+/* Parse encoded descriptor; TODO add possibility to parse multiple descs from
+ * one string for replication purposes! */
+int
+rend_parse_v2_service_descriptor(rend_service_descriptor_t *result,
+                                 const char *s)
+{
+
+  const char *end;
+  char digesthash[128];
+  int protocols;
+  smartlist_t *tokens = NULL;
+  directory_token_t *tok;
+  uint8_t version;
+
+  /* point 'end' to a point immediately after the final newline. */
+  end = s + strlen(s);
+  while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n')
+    --end;
+
+  /* compute descriptor hash */
+  if (router_get_hash_impl(s,digesthash, "rendezvous-service-descriptor ",
+                           "\nsignature", '\n') < 0) {
+    log_warn(LD_DIR, "Couldn't compute descriptor hash.");
+    return -1;
+  }
+
+  /* tokenize descriptor */
+  tokens = smartlist_create();
+  if (tokenize_string(s,end,tokens,desc_token_table)) {
+    log_warn(LD_DIR, "Error tokenizing descriptor.");
+    goto err;
+  }
+
+  /* check min allowed length of token list */
+  if (smartlist_len(tokens) < 8) {
+    log_warn(LD_DIR, "Impossibly short descriptor.");
+    goto err;
+  }
+
+  /* check whether descriptor starts correctly */
+  tok = smartlist_get(tokens,0);
+  if (tok->tp != R_RENDEZVOUS_SERVICE_DESCRIPTOR) {
+    log_warn(LD_DIR,"Entry does not start with "
+             "\"rendezvous-service-descriptor\"");
+    goto err;
+  }
+
+  /* parse publication time; TODO check if up-to-date */
+  tok = find_first_by_keyword(tokens, R_PUBLICATION_TIME);
+  tor_assert(tok);
+  tor_assert(tok->n_args == 1);
+  if (parse_iso_time(tok->args[0], &result->timestamp) < 0)
+    goto err;
+
+  /* parse version */
+  tok = find_first_by_keyword(tokens, R_VERSION);
+  version = atoi(tok->args[0]);
+  if (version != 2) {
+    log_warn(LD_DIR, "wrong descriptor version: %d", version);
+    goto err;
+  }
+
+  /* parse public key */
+  tok = find_first_by_keyword(tokens, R_PERMANENT_KEY);
+  tor_assert(tok);
+  result->pk = tok->key;
+  tok->key = NULL; /* Prevent free */
+
+  /* parse secret ID part */
+  tok = find_first_by_keyword(tokens, R_SECRET_ID_PART);
+  tor_assert(tok->n_args == 1);
+  if (base32_decode(result->secret_id_part[0], 20, tok->args[0],
+                    strlen(tok->args[0]))) {
+    log_warn(LD_DIR, "Couldn't decode secret id part %s",
+             escaped(tok->args[0]));
+    goto err;
+  }
+
+  /* parse descriptor ID */
+  tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
+  tor_assert(tok->n_args == 1);
+  if (base32_decode(result->desc_id[0], 20, tok->args[0],
+                    strlen(tok->args[0]))) {
+    log_warn(LD_DIR, "Couldn't decode desc id %s",
+             escaped(tok->args[0]));
+    goto err;
+  }
+
+  /* parse encrypted introduction points */
+  tok = find_first_by_keyword(tokens, R_INTRODUCTION_POINTS);
+  result->intro_points_encrypted = tok->object_body;
+  tok->object_body = NULL; /* Prevent free */
+  result->intro_points_encrypted_size = tok->object_size;
+
+  /* parse descriptor version */
+  tok = find_first_by_keyword(tokens, R_PROTOCOL_VERSIONS);
+  log_warn(LD_DIR, "parsed protocol versions: %s", tok->args[0]);
+  /* for the moment, use version 1 to keep things working TODO change */
+  result->version = 1;
+
+  /* parse protocol versions */
+  protocols = atoi(tok->args[0]);
+  result->protocols = protocols;
+
+  /* parse and verify signature */
+  tok = find_first_by_keyword(tokens, R_SIGNATURE);
+  tor_assert(tok);
+  note_crypto_pk_op(VERIFY_RTR);
+  if (check_signature_token(digesthash, tok, result->pk, 0,
+                            "rendezvous service descriptor") < 0)
+    goto err;
+
+  goto done;
+
+ err:
+  /* TODO free some memory */
+
+ done:
+  /* TODO free smartlists and stuff */
+
+  return 0;
+}
+
+int
+rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
+                                 const char *secret_cookie)
+{
+  smartlist_t *intropoints; /* smartlist for parsed intro points */
+  char key[16]; /* decryption key */
+  char *ipos_decrypted; /* decrypted intro points */
+  int unenclen; /* length of unencrypted intro points */
+  smartlist_t *tokens; /* smartlist for intro point tokens */
+  const char **s2; /* currently investigated intro point string */
+  int i;
+
+  /* create smartlists */
+  tokens = smartlist_create();
+  intropoints = smartlist_create();
+
+  /* decrypt introduction points and store info in parsed */
+  ipos_decrypted = tor_malloc_zero(parsed->intro_points_encrypted_size);
+  memcpy(key, secret_cookie, 15);
+  key[15] = 0;
+  unenclen = crypto_cipher_decrypt_cbc(key, ipos_decrypted,
+                                       parsed->intro_points_encrypted,
+                                       parsed->intro_points_encrypted_size);
+
+  /* consider one intro point after the other */
+  s2 = (const char **)&ipos_decrypted;
+  while (!strcmpstart(*s2, "introduction-point ")) {
+
+    const char *eos; /* end position of string */ 
+    directory_token_t *tok; /* single token */
+    extend_info_t *info; /* extend info to write parsed result to */
+    struct in_addr in; /* IP address */
+
+    /* determine end of string */
+    eos = strstr(*s2, "\nintroduction-point ");
+    if (!eos)
+      eos = *s2+strlen(*s2);
+    else
+      eos = eos+1;
+
+    /* free old tokens, if available, and clear token list */ 
+    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+    smartlist_clear(tokens);
+
+    /* tokenize string */
+    if (tokenize_string(*s2, eos, tokens, ipo_token_table)) {
+      log_warn(LD_DIR, "Error tokenizing introduction point");
+      goto err;
+    }
+    
+    /* check minimum allowed length of introduction point */
+    if (smartlist_len(tokens) < 5) {
+      log_warn(LD_DIR, "Impossibly short introduction point");
+      goto err;
+    }
+    
+    /* allocate new extend info */
+    info = tor_malloc_zero(sizeof(extend_info_t));
+
+    /* parse identifier */
+    tok = find_first_by_keyword(tokens, R_IPO_IDENTIFIER);
+    tor_assert(tok);
+
+    /* base32-decode identifier and store it */
+    base32_decode(info->identity_digest, DIGEST_LEN, tok->args[0], 32);
+
+    /* write identifier to nickname */
+    info->nickname[0] = '$';
+    base16_encode(info->nickname+1, sizeof(info->nickname)-1,
+                  info->identity_digest, DIGEST_LEN);
+
+    /* parse IP address */
+    tok = find_first_by_keyword(tokens, R_IPO_IP_ADDRESS);
+    if (tor_inet_aton(tok->args[0], &in) != 0) {
+      info->addr = ntohl(in.s_addr);
+    } else {
+      log_warn(LD_DIR, "could not parse IP address");
+    }
+
+    /* parse onion port */
+    tok = find_first_by_keyword(tokens, R_IPO_ONION_PORT);
+    info->port = (uint16_t) atoi(tok->args[0]);
+
+    /* parse onion key */
+    tok = find_first_by_keyword(tokens, R_IPO_ONION_KEY);
+    info->onion_key = tok->key;
+    tok->key = NULL; /* Prevent free */
+
+    /* add extend info to list of introduction points */
+    smartlist_add(intropoints, info);
+
+    /* advance to next introduction point, if available */
+    *s2 = eos;
+  }
+
+  /* write extend infos to descriptor */
+  parsed->n_intro_points = smartlist_len(intropoints);
+  parsed->intro_point_extend_info =
+    tor_malloc_zero(sizeof(extend_info_t*) * parsed->n_intro_points);
+  parsed->intro_points =
+    tor_malloc_zero(sizeof(char*) * parsed->n_intro_points);
+
+  i = 0;
+  SMARTLIST_FOREACH(intropoints, extend_info_t *, ipo, {
+      parsed->intro_points[i] = tor_strdup(ipo->nickname);
+      parsed->intro_point_extend_info[i++] = ipo;
+    });
+
+  goto done;
+
+ err:
+  /* TODO free some memory */
+
+ done:
+  /* TODO free smartlists and stuff */
+
+  return 0;
+}
+