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

[or-cvs] First round of incompatible changes for key rotation. (Thi...



Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/tmp/cvs-serv12855/src/or

Modified Files:
      Tag: tor-0_0_6incompat
	connection.c connection_or.c cpuworker.c onion.c or.h router.c 
	routerlist.c test.c 
Log Message:
First round of incompatible changes for key rotation.  (This is in the
incompat branch, so all is well with the Tor network.)  The basic
functionality for link pkey and onion pkey rotation is both in, but I
changed it a lot since the last version that even kinda worked, so it
probably needs more testing.  Which I'll do now.

Still not in for key rotation:
   - Key rotation is never actually invoked.
   - Connection rotation doesn't really happen.
   - The log messages on failed pkey decryption probably need finetuning.
   - Windows needs locking code to work.



Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection.c,v
retrieving revision 1.195
retrieving revision 1.195.2.1
diff -u -d -r1.195 -r1.195.2.1
--- connection.c	8 Apr 2004 07:25:54 -0000	1.195
+++ connection.c	17 Apr 2004 01:37:27 -0000	1.195.2.1
@@ -116,8 +116,6 @@
 
   if (conn->onion_pkey)
     crypto_free_pk_env(conn->onion_pkey);
-  if (conn->link_pkey)
-    crypto_free_pk_env(conn->link_pkey);
   if (conn->identity_pkey)
     crypto_free_pk_env(conn->identity_pkey);
   tor_free(conn->nickname);

Index: connection_or.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_or.c,v
retrieving revision 1.94
retrieving revision 1.94.2.1
diff -u -d -r1.94 -r1.94.2.1
--- connection_or.c	8 Apr 2004 19:49:55 -0000	1.94
+++ connection_or.c	17 Apr 2004 01:37:27 -0000	1.94.2.1
@@ -84,7 +84,6 @@
   conn->port = router->or_port;
   conn->receiver_bucket = conn->bandwidth = router->bandwidthburst;
   conn->onion_pkey = crypto_pk_dup_key(router->onion_pkey);
-  conn->link_pkey = crypto_pk_dup_key(router->link_pkey);
   conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey);
   conn->nickname = tor_strdup(router->nickname);
   tor_free(conn->address);
@@ -178,7 +177,6 @@
 }
 
 static int connection_tls_finish_handshake(connection_t *conn) {
-  crypto_pk_env_t *pk;
   routerinfo_t *router;
   char nickname[MAX_NICKNAME_LEN+1];
   connection_t *c;
@@ -203,36 +201,23 @@
     return -1;
   }
   log_fn(LOG_DEBUG, "Other side claims to be '%s'", nickname);
-  pk = tor_tls_verify(conn->tls);
-  if(!pk) {
-    log_fn(LOG_WARN,"Other side '%s' (%s:%d) has a cert but it's invalid. Closing.",
-           nickname, conn->address, conn->port);
+  router = router_get_by_nickname(nickname);
+  if (!router) {
+    log_fn(LOG_INFO, "Unrecognized router with nickname '%s'", nickname);
     return -1;
   }
-  router = router_get_by_link_pk(pk);
-  if (!router) {
-    log_fn(LOG_INFO,"Unrecognized public key from peer '%s' (%s:%d). Closing.",
+  if(tor_tls_verify(conn->tls, router->identity_pkey)<0) {
+    log_fn(LOG_WARN,"Other side '%s' (%s:%d) has a cert but it's invalid. Closing.",
            nickname, conn->address, conn->port);
-    crypto_free_pk_env(pk);
     return -1;
   }
-  if(conn->link_pkey) { /* I initiated this connection. */
-    if(crypto_pk_cmp_keys(conn->link_pkey, pk)) {
-      log_fn(LOG_WARN,"We connected to '%s' (%s:%d) but he gave us a different key. Closing.",
-             nickname, conn->address, conn->port);
-      crypto_free_pk_env(pk);
-      return -1;
-    }
-    log_fn(LOG_DEBUG,"The router's pk matches the one we meant to connect to. Good.");
-  } else {
-    if((c=connection_exact_get_by_addr_port(router->addr,router->or_port))) {
-      log_fn(LOG_INFO,"Router %s is already connected on fd %d. Dropping fd %d.", router->nickname, c->s, conn->s);
-      crypto_free_pk_env(pk);
-      return -1;
-    }
-    connection_or_init_conn_from_router(conn, router);
+  log_fn(LOG_DEBUG,"The router's cert is valid.");
+
+  if((c=connection_exact_get_by_addr_port(router->addr,router->or_port))) {
+    log_fn(LOG_INFO,"Router %s is already connected on fd %d. Dropping fd %d.", router->nickname, c->s, conn->s);
+    return -1;
   }
-  crypto_free_pk_env(pk);
+
   if (strcmp(conn->nickname, nickname)) {
     log_fn(options.DirPort ? LOG_WARN : LOG_INFO,
            "Other side claims to be '%s', but we expected '%s'",

Index: cpuworker.c
===================================================================
RCS file: /home/or/cvsroot/src/or/cpuworker.c,v
retrieving revision 1.28
retrieving revision 1.28.2.1
diff -u -d -r1.28 -r1.28.2.1
--- cpuworker.c	20 Mar 2004 09:30:30 -0000	1.28
+++ cpuworker.c	17 Apr 2004 01:37:27 -0000	1.28.2.1
@@ -12,8 +12,9 @@
 #define LEN_ONION_QUESTION (1+TAG_LEN+ONIONSKIN_CHALLENGE_LEN)
 #define LEN_ONION_RESPONSE (1+TAG_LEN+ONIONSKIN_REPLY_LEN+40+32)
 
-int num_cpuworkers=0;
-int num_cpuworkers_busy=0;
+static int num_cpuworkers=0;
+static int num_cpuworkers_busy=0;
+static time_t last_rotation_time=0;
 
 int cpuworker_main(void *data);
 static int spawn_cpuworker(void);
@@ -21,6 +22,7 @@
 static void process_pending_task(connection_t *cpuworker);
 
 void cpu_init(void) {
+  last_rotation_time=time(NULL);
   spawn_enough_cpuworkers();
 }
 
@@ -47,6 +49,18 @@
   log_fn(LOG_DEBUG,"onion was from %s:%d, circ_id %d.", inet_ntoa(in), *port, *circ_id);
 }
 
+void cpuworkers_rotate(void)
+{
+  connection_t *cpuworker;
+  while ((cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER,
+                                                   CPUWORKER_STATE_IDLE))) {
+    connection_mark_for_close(cpuworker,0);
+    --num_cpuworkers;
+  }
+  last_rotation_time = time(NULL);
+  spawn_enough_cpuworkers();
+}
+
 int connection_cpu_process_inbuf(connection_t *conn) {
   char success;
   unsigned char buf[LEN_ONION_RESPONSE];
@@ -111,7 +125,13 @@
 done_processing:
   conn->state = CPUWORKER_STATE_IDLE;
   num_cpuworkers_busy--;
-  process_pending_task(conn);
+  if (conn->timestamp_created < last_rotation_time) {
+    connection_mark_for_close(conn,0);
+    num_cpuworkers--;
+    spawn_enough_cpuworkers();
+  } else {
+    process_pending_task(conn);
+  }
   return 0;
 }
 
@@ -126,6 +146,7 @@
   unsigned char reply_to_proxy[ONIONSKIN_REPLY_LEN];
   unsigned char buf[LEN_ONION_RESPONSE];
   char tag[TAG_LEN];
+  crypto_pk_env_t *onion_key = NULL, *last_onion_key = NULL;
 
   close(fdarray[0]); /* this is the side of the socketpair the parent uses */
   fd = fdarray[1]; /* this side is ours */
@@ -133,27 +154,32 @@
   connection_free_all(); /* so the child doesn't hold the parent's fd's open */
 #endif
 
+  /* XXXX WINDOWS lock here. */
+  onion_key = crypto_pk_dup_key(get_onion_key());
+  if (get_previous_onion_key())
+    last_onion_key = crypto_pk_dup_key(get_previous_onion_key());
+
   for(;;) {
 
     if(recv(fd, &question_type, 1, 0) != 1) {
 //      log_fn(LOG_ERR,"read type failed. Exiting.");
       log_fn(LOG_INFO,"cpuworker exiting because tor process died.");
-      spawn_exit();
+      goto end;
     }
     assert(question_type == CPUWORKER_TASK_ONION);
 
     if(read_all(fd, tag, TAG_LEN, 1) != TAG_LEN) {
       log_fn(LOG_ERR,"read tag failed. Exiting.");
-      spawn_exit();
+      goto end;
     }
 
     if(read_all(fd, question, ONIONSKIN_CHALLENGE_LEN, 1) != ONIONSKIN_CHALLENGE_LEN) {
       log_fn(LOG_ERR,"read question failed. Exiting.");
-      spawn_exit();
+      goto end;
     }
 
     if(question_type == CPUWORKER_TASK_ONION) {
-      if(onion_skin_server_handshake(question, get_onion_key(),
+      if(onion_skin_server_handshake(question, onion_key, last_onion_key,
         reply_to_proxy, keys, 40+32) < 0) {
         /* failure */
         log_fn(LOG_WARN,"onion_skin_server_handshake failed.");
@@ -173,6 +199,12 @@
       log_fn(LOG_DEBUG,"finished writing response.");
     }
   }
+ end:
+  if (onion_key)
+    crypto_free_pk_env(onion_key);
+  if (last_onion_key)
+    crypto_free_pk_env(last_onion_key);
+  spawn_exit();
   return 0; /* windows wants this function to return an int */
 }
 
@@ -263,7 +295,7 @@
       return 0;
     }
 
-    if(!cpuworker)
+    if (!cpuworker)
       cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER, CPUWORKER_STATE_IDLE);
 
     assert(cpuworker);

Index: onion.c
===================================================================
RCS file: /home/or/cvsroot/src/or/onion.c,v
retrieving revision 1.154
retrieving revision 1.154.2.1
diff -u -d -r1.154 -r1.154.2.1
--- onion.c	14 Apr 2004 05:06:08 -0000	1.154
+++ onion.c	17 Apr 2004 01:37:27 -0000	1.154.2.1
@@ -575,9 +575,6 @@
 [16 bytes] Symmetric key for encrypting blob past RSA
 [112 bytes] g^x part 1 (inside the RSA)
 [16 bytes] g^x part 2 (symmetrically encrypted)
-[ 6 bytes] Meeting point (IP/port)
-[ 8 bytes] Meeting cookie
-[16 bytes] End-to-end authentication [optional]
 
  * Stores the DH private key into handshake_state_out for later completion
  * of the handshake.
@@ -603,7 +600,7 @@
   pkbytes = crypto_pk_keysize(dest_router_key);
   assert(dhbytes == 128);
   assert(pkbytes == 128);
-  challenge = tor_malloc_zero(ONIONSKIN_CHALLENGE_LEN-CIPHER_KEY_LEN);
+  challenge = tor_malloc_zero(DH_KEY_LEN);
 
   if (crypto_dh_get_public(dh, challenge, dhbytes))
     goto err;
@@ -625,8 +622,8 @@
 
   /* set meeting point, meeting cookie, etc here. Leave zero for now. */
   if (crypto_pk_public_hybrid_encrypt(dest_router_key, challenge,
-                                      ONIONSKIN_CHALLENGE_LEN-CIPHER_KEY_LEN,
-                                      onion_skin_out, PK_NO_PADDING, 1)<0)
+                                  ONIONSKIN_CHALLENGE_LEN-CIPHER_KEY_LEN,
+                                  onion_skin_out, PK_PKCS1_OAEP_PADDING, 1)<0)
     goto err;
 
   tor_free(challenge);
@@ -647,6 +644,7 @@
 int
 onion_skin_server_handshake(char *onion_skin, /* ONIONSKIN_CHALLENGE_LEN bytes */
                             crypto_pk_env_t *private_key,
+                            crypto_pk_env_t *prev_private_key,
                             char *handshake_reply_out, /* ONIONSKIN_REPLY_LEN bytes */
                             char *key_out,
                             int key_out_len)
@@ -655,11 +653,28 @@
   crypto_dh_env_t *dh = NULL;
   int len;
   char *key_material=NULL;
+  int i;
+  crypto_pk_env_t *k;
 
-  if (crypto_pk_private_hybrid_decrypt(private_key,
-                                       onion_skin, ONIONSKIN_CHALLENGE_LEN,
-                                       challenge, PK_NO_PADDING)<0)
+  len = -1;
+  for (i=0;i<2;++i) {
+    k = i==0?private_key:prev_private_key;
+    if (!k)
+      break;
+    len = crypto_pk_private_hybrid_decrypt(k,
+                                           onion_skin, ONIONSKIN_CHALLENGE_LEN,
+                                           challenge, PK_PKCS1_OAEP_PADDING);
+    if (len>0)
+      break;
+  }
+  if (len<0) {
+    log_fn(LOG_WARN, "Couldn't decrypt onionskin");
+    goto err;
+  } else if (len != DH_KEY_LEN) {
+    log_fn(LOG_WARN, "Unexpected onionskin length after decryption: %d",
+           len);
     goto err;
+  }
 
   dh = crypto_dh_new();
   if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN))

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.319
retrieving revision 1.319.2.1
diff -u -d -r1.319 -r1.319.2.1
--- or.h	14 Apr 2004 21:40:50 -0000	1.319
+++ or.h	17 Apr 2004 01:37:28 -0000	1.319.2.1
@@ -377,7 +377,6 @@
                   * strdup into this, because free_connection frees it
                   */
   crypto_pk_env_t *onion_pkey; /* public RSA key for the other side's onions */
-  crypto_pk_env_t *link_pkey; /* public RSA key for the other side's TLS */
   crypto_pk_env_t *identity_pkey; /* public RSA key for the other side's signing */
   char *nickname;
 
@@ -441,7 +440,6 @@
   time_t published_on;
 
   crypto_pk_env_t *onion_pkey; /* public RSA key for onions */
-  crypto_pk_env_t *link_pkey;  /* public RSA key for TLS */
   crypto_pk_env_t *identity_pkey;  /* public RSA key for signing */
 
   int is_running;
@@ -488,8 +486,10 @@
 };
 
 #define DH_KEY_LEN DH_BYTES
-#define ONIONSKIN_CHALLENGE_LEN (16+DH_KEY_LEN)
-#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+20)
+#define ONIONSKIN_CHALLENGE_LEN (PKCS1_OAEP_PADDING_OVERHEAD+\
+                                 CIPHER_KEY_LEN+\
+                                 DH_KEY_LEN)
+#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN)
 #define REND_COOKIE_LEN DIGEST_LEN
 
 typedef struct crypt_path_t crypt_path_t;
@@ -878,6 +878,7 @@
 /********************************* cpuworker.c *****************************/
 
 void cpu_init(void);
+void cpuworkers_rotate(void);
 int connection_cpu_finished_flushing(connection_t *conn);
 int connection_cpu_process_inbuf(connection_t *conn);
 int cpuworker_main(void *data);
@@ -944,6 +945,7 @@
 
 int onion_skin_server_handshake(char *onion_skin,
                                 crypto_pk_env_t *private_key,
+                                crypto_pk_env_t *prev_private_key,
                                 char *handshake_reply_out,
                                 char *key_out,
                                 int key_out_len);
@@ -960,11 +962,12 @@
 
 void set_onion_key(crypto_pk_env_t *k);
 crypto_pk_env_t *get_onion_key(void);
+crypto_pk_env_t *get_previous_onion_key(void);
 void set_identity_key(crypto_pk_env_t *k);
 crypto_pk_env_t *get_identity_key(void);
-crypto_pk_env_t *get_link_key(void);
 int init_keys(void);
 crypto_pk_env_t *init_key_from_file(const char *fname);
+void rotate_onion_key(void);
 
 void router_retry_connections(void);
 void router_upload_dir_desc_to_dirservers(void);
@@ -985,7 +988,6 @@
                                         char *preferred, char *excluded,
                                         struct smartlist_t *excludedsmartlist);
 routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port);
-routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk);
 routerinfo_t *router_get_by_nickname(char *nickname);
 void router_get_routerlist(routerlist_t **prouterlist);
 void routerinfo_free(routerinfo_t *router);

Index: router.c
===================================================================
RCS file: /home/or/cvsroot/src/or/router.c,v
retrieving revision 1.26
retrieving revision 1.26.2.1
diff -u -d -r1.26 -r1.26.2.1
--- router.c	8 Apr 2004 20:22:01 -0000	1.26
+++ router.c	17 Apr 2004 01:37:28 -0000	1.26.2.1
@@ -12,7 +12,7 @@
 
 /* private keys */
 static crypto_pk_env_t *onionkey=NULL;
-static crypto_pk_env_t *linkkey=NULL;
+static crypto_pk_env_t *lastonionkey=NULL;
 static crypto_pk_env_t *identitykey=NULL;
 
 void set_onion_key(crypto_pk_env_t *k) {
@@ -24,15 +24,8 @@
   return onionkey;
 }
 
-void set_link_key(crypto_pk_env_t *k)
-{
-  linkkey = k;
-}
-
-crypto_pk_env_t *get_link_key(void)
-{
-  assert(linkkey);
-  return linkkey;
+crypto_pk_env_t *get_previous_onion_key(void) {
+  return lastonionkey;
 }
 
 void set_identity_key(crypto_pk_env_t *k) {
@@ -46,6 +39,46 @@
 
 /************************************************************/
 
+/* Replace the previous onion key with the current onion key, and generate
+ * a new previous onion key.  Immediately after calling this function,
+ * the OR should:
+ *     a) shedule all previous cpuworker to shut down _after_ processing
+ *        pending work.  (This will cause fresh cpuworkers to be generated.)
+ *     b) generate and upload a fresh routerinfo.
+ */
+void rotate_onion_key(void)
+{
+  char fname[512];
+  crypto_pk_env_t *prkey;
+  sprintf(fname,"%s/keys/onion.key",options.DataDirectory);
+  if (!(prkey = crypto_new_pk_env())) {
+    log(LOG_ERR, "Error creating crypto environment.");
+    goto error;
+  }
+  if (crypto_pk_generate_key(onionkey)) {
+    log(LOG_ERR, "Error generating key: %s", crypto_perror());
+    goto error;
+  }
+  if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
+    log(LOG_ERR, "Couldn't write generated key to %s.", fname);
+    goto error;
+  }
+  if (lastonionkey)
+    crypto_free_pk_env(lastonionkey);
+  /* XXXX WINDOWS on windows, we need to protect this next bit with a lock.
+   */
+  lastonionkey = onionkey;
+  onionkey = prkey;
+  if (router_rebuild_descriptor() <0) {
+    goto error;
+  }
+  router_upload_dir_desc_to_dirservers();
+  /* Mark all CPU workers to close. */
+  return;
+ error:
+  log_fn(LOG_WARN, "Couldn't rotate onion key.");
+}
+
 /* Try to read an RSA key from 'fname'.  If 'fname' doesn't exist, create a new
  * RSA key and save it in 'fname'.  Return the read/created key, or NULL on
  * error.
@@ -146,12 +179,7 @@
   set_onion_key(prkey);
 
   /* 3. Initialize link key and TLS context. */
-  strcpy(cp, "/link.key");
-  log_fn(LOG_INFO,"Reading/making link key %s...",keydir);
-  prkey = init_key_from_file(keydir);
-  if (!prkey) return -1;
-  set_link_key(prkey);
-  if (tor_tls_context_new(prkey, 1, options.Nickname) < 0) {
+  if (tor_tls_context_new(get_identity_key(), 1, options.Nickname) < 0) {
     log_fn(LOG_ERR, "Error initializing TLS context");
     return -1;
   }
@@ -370,7 +398,6 @@
   ri->dir_port = options.DirPort;
   ri->published_on = time(NULL);
   ri->onion_pkey = crypto_pk_dup_key(get_onion_key());
-  ri->link_pkey = crypto_pk_dup_key(get_link_key());
   ri->identity_pkey = crypto_pk_dup_key(get_identity_key());
   get_platform_str(platform, sizeof(platform));
   ri->platform = tor_strdup(platform);
@@ -403,13 +430,12 @@
 int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
                                  crypto_pk_env_t *ident_key) {
   char *onion_pkey;
-  char *link_pkey;
   char *identity_pkey;
   struct in_addr in;
   char digest[20];
   char signature[128];
   char published[32];
-  int onion_pkeylen, link_pkeylen, identity_pkeylen;
+  int onion_pkeylen, identity_pkeylen;
   int written;
   int result=0;
   struct exit_policy_t *tmpe;
@@ -436,19 +462,14 @@
     return -1;
   }
 
-  if(crypto_pk_write_public_key_to_string(router->link_pkey,
-                                          &link_pkey,&link_pkeylen)<0) {
-    log_fn(LOG_WARN,"write link_pkey to string failed!");
-    return -1;
-  }
   strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&router->published_on));
 
+  /* XXXX eventually, don't include link key */
   result = snprintf(s, maxlen,
                     "router %s %s %d %d %d %d\n"
                     "platform %s\n"
                     "published %s\n"
                     "onion-key\n%s"
-                    "link-key\n%s"
                     "signing-key\n%s",
     router->nickname,
     router->address,
@@ -459,10 +480,9 @@
 /* XXXBC also write bandwidthburst */
     router->platform,
     published,
-    onion_pkey, link_pkey, identity_pkey);
+    onion_pkey, identity_pkey);
 
   free(onion_pkey);
-  free(link_pkey);
   free(identity_pkey);
 
   if(result < 0 || result >= maxlen) {

Index: routerlist.c
===================================================================
RCS file: /home/or/cvsroot/src/or/routerlist.c,v
retrieving revision 1.60
retrieving revision 1.60.2.1
diff -u -d -r1.60 -r1.60.2.1
--- routerlist.c	8 Apr 2004 03:21:15 -0000	1.60
+++ routerlist.c	17 Apr 2004 01:37:28 -0000	1.60.2.1
@@ -30,7 +30,7 @@
   K_SIGNED_DIRECTORY,
   K_SIGNING_KEY,
   K_ONION_KEY,
-  K_LINK_KEY,
+  K_LINK_KEY, /* XXXX obsolete */
   K_ROUTER_SIGNATURE,
   K_PUBLISHED,
   K_RUNNING_ROUTERS,
@@ -305,21 +305,6 @@
   return NULL;
 }
 
-routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk)
-{
-  int i;
-  routerinfo_t *router;
-
-  assert(routerlist);
-
-  for(i=0;i<smartlist_len(routerlist->routers);i++) {
-    router = smartlist_get(routerlist->routers, i);
-    if (0 == crypto_pk_cmp_keys(router->link_pkey, pk))
-      return router;
-  }
-  return NULL;
-}
-
 routerinfo_t *router_get_by_nickname(char *nickname)
 {
   int i;
@@ -354,8 +339,6 @@
   tor_free(router->platform);
   if (router->onion_pkey)
     crypto_free_pk_env(router->onion_pkey);
-  if (router->link_pkey)
-    crypto_free_pk_env(router->link_pkey);
   if (router->identity_pkey)
     crypto_free_pk_env(router->identity_pkey);
   while (router->exit_policy) {
@@ -380,8 +363,6 @@
   r->platform = tor_strdup(r->platform);
   if (r->onion_pkey)
     r->onion_pkey = crypto_pk_dup_key(r->onion_pkey);
-  if (r->link_pkey)
-    r->link_pkey = crypto_pk_dup_key(r->link_pkey);
   if (r->identity_pkey)
     r->identity_pkey = crypto_pk_dup_key(r->identity_pkey);
   e = &r->exit_policy;
@@ -949,7 +930,7 @@
   }
 
   router = tor_malloc_zero(sizeof(routerinfo_t));
-  router->onion_pkey = router->identity_pkey = router->link_pkey = NULL;
+  router->onion_pkey = router->identity_pkey = NULL;
   ports_set = bw_set = 0;
 
   if (tok->n_args == 2 || tok->n_args == 6) {
@@ -1020,12 +1001,9 @@
   router->onion_pkey = tok->key;
   tok->key = NULL; /* Prevent free */
 
-  if (!(tok = find_first_by_keyword(tokens, K_LINK_KEY))) {
-    log_fn(LOG_WARN, "Missing onion key"); goto err;
+  if ((tok = find_first_by_keyword(tokens, K_LINK_KEY))) {
+    log_fn(LOG_INFO, "Skipping obsolete link-key"); goto err;
   }
-  /* XXX Check key length */
-  router->link_pkey = tok->key;
-  tok->key = NULL; /* Prevent free */
 
   if (!(tok = find_first_by_keyword(tokens, K_SIGNING_KEY))) {
     log_fn(LOG_WARN, "Missing onion key"); goto err;

Index: test.c
===================================================================
RCS file: /home/or/cvsroot/src/or/test.c,v
retrieving revision 1.81
retrieving revision 1.81.2.1
diff -u -d -r1.81 -r1.81.2.1
--- test.c	14 Apr 2004 19:51:57 -0000	1.81
+++ test.c	17 Apr 2004 01:37:28 -0000	1.81.2.1
@@ -650,7 +650,7 @@
   /* server handshake */
   memset(s_buf, 0, ONIONSKIN_REPLY_LEN);
   memset(s_keys, 0, 40);
-  test_assert(! onion_skin_server_handshake(c_buf, pk, s_buf, s_keys, 40));
+  test_assert(! onion_skin_server_handshake(c_buf, pk, NULL, s_buf, s_keys, 40));
 
   /* client handshake 2 */
   memset(c_keys, 0, 40);
@@ -701,7 +701,6 @@
   r1.dir_port = 9003;
   r1.onion_pkey = pk1;
   r1.identity_pkey = pk2;
-  r1.link_pkey = pk3;
   r1.bandwidthrate = r1.bandwidthburst = 1000;
   r1.exit_policy = NULL;
   r1.nickname = "Magri";
@@ -727,7 +726,6 @@
   r2.dir_port = 0;
   r2.onion_pkey = pk2;
   r2.identity_pkey = pk1;
-  r2.link_pkey = pk2;
   r2.bandwidthrate = r2.bandwidthburst = 3000;
   r2.exit_policy = &ex1;
   r2.nickname = "Fred";
@@ -749,8 +747,6 @@
          "published 1970-01-01 00:00:00\n"
          "onion-key\n");
   strcat(buf2, pk1_str);
-  strcat(buf2, "link-key\n");
-  strcat(buf2, pk3_str);
   strcat(buf2, "signing-key\n");
   strcat(buf2, pk2_str);
   strcat(buf2, "router-signature\n");
@@ -769,7 +765,6 @@
   test_eq(rp1->bandwidthrate, r1.bandwidthrate);
 //  test_eq(rp1->bandwidthburst, r1.bandwidthburst);
   test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
-  test_assert(crypto_pk_cmp_keys(rp1->link_pkey, pk3) == 0);
   test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
   test_assert(rp1->exit_policy == NULL);