Bläddra i källkod

More work on directories. Signed directories not yet tested. No support for checking sigs yet

svn:r268
Nick Mathewson 21 år sedan
förälder
incheckning
d0ff485e1b
7 ändrade filer med 218 tillägg och 30 borttagningar
  1. 2 2
      doc/TODO
  2. 65 0
      src/common/crypto.c
  3. 5 0
      src/common/crypto.h
  4. 88 15
      src/or/main.c
  5. 3 0
      src/or/or.h
  6. 36 8
      src/or/routers.c
  7. 19 5
      src/or/test.c

+ 2 - 2
doc/TODO

@@ -83,8 +83,8 @@ SPEC!!          - Handle socks commands other than connect, eg, bind?
                         o Cells
                 . Better comments for functions!
         - Tests
-NICK            o Testing harness/infrastructure
-                . Unit tests
+                o Testing harness/infrastructure
+NICK            . Unit tests
                 D System tests (how?)
                 - Performance tests, so we know when we've improved
                         . webload infrastructure (Bruce)

+ 65 - 0
src/common/crypto.c

@@ -545,6 +545,36 @@ int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fro
   }
 }
 
+int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to)
+{
+  assert(env && from && to);
+
+  switch(env->type) {
+  case CRYPTO_PK_RSA:
+    if (!(((RSA*)env->key)->p))
+      return -1;
+    return RSA_public_decrypt(fromlen, from, to, (RSA *)env->key, 
+			      RSA_PKCS1_OAEP_PADDING);
+    default:
+    return -1;
+  }
+}
+
+int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to)
+{
+  assert(env && from && to);
+
+  switch(env->type) {
+  case CRYPTO_PK_RSA:
+    if (!(((RSA*)env->key)->p))
+      return -1;
+    return RSA_private_encrypt(fromlen, from, to, (RSA *)env->key, 
+			       RSA_PKCS1_OAEP_PADDING);
+    default:
+    return -1;
+  }
+}
+
 /* symmetric crypto */
 int crypto_cipher_generate_key(crypto_cipher_env_t *env)
 {
@@ -779,3 +809,38 @@ char *crypto_perror()
   return (char *)ERR_reason_error_string(ERR_get_error());
 }
 
+int 
+base64_encode(char *dest, int destlen, char *src, int srclen)
+{
+  EVP_ENCODE_CTX ctx;
+  int len, ret;
+  
+  /* 48 bytes of input -> 64 bytes of output plus newline. 
+     Plus one more byte, in case I'm wrong.
+  */
+  if (destlen < ((srclen/48)+1)*66)
+    return -1;
+
+  EVP_EncodeInit(&ctx);
+  EVP_EncodeUpdate(&ctx, dest, &len, src, srclen);
+  EVP_EncodeFinal(&ctx, dest, &ret);
+  ret += len;
+  return ret;
+}
+int 
+base64_decode(char *dest, int destlen, char *src, int srclen)
+{
+  EVP_ENCODE_CTX ctx;
+  int len, ret;
+  /* 64 bytes of input -> *up to* 48 bytes of output.
+     Plus one more byte, in caes I'm wrong.
+  */
+  if (destlen < ((srclen/64)+1)*49)
+    return -1;
+
+  EVP_DecodeInit(&ctx);
+  EVP_DecodeUpdate(&ctx, dest, &len, src, srclen);
+  EVP_DecodeFinal(&ctx, dest, &ret);
+  ret += len;
+  return ret;
+}

+ 5 - 0
src/common/crypto.h

@@ -64,6 +64,11 @@ int crypto_pk_keysize(crypto_pk_env_t *env);
 
 int crypto_pk_public_encrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
 int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
+int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
+int crypto_pk_private_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
+
+int base64_encode(char *dest, int destlen, char *src, int srclen);
+int base64_decode(char *dest, int destlen, char *src, int srclen);
 
 /* Key negotiation */
 typedef struct crypto_dh_env_st crypto_dh_env_t;

+ 88 - 15
src/or/main.c

@@ -555,7 +555,8 @@ void dumpstats(void) { /* dump stats to stdout */
 
 int dump_router_to_string(char *s, int maxlen, routerinfo_t *router) {
   char *pkey;
-  int pkeylen;
+  char *signing_pkey, *signing_pkey_tag;
+  int pkeylen, signing_pkeylen;
   int written;
   int result=0;
   struct exit_policy_t *tmpe;
@@ -565,16 +566,30 @@ int dump_router_to_string(char *s, int maxlen, routerinfo_t *router) {
     return 0;
   }
 
-  result = snprintf(s, maxlen, "router %s %d %d %d %d %d\n%s",
+  signing_pkey = "";
+  signing_pkey_tag = "";
+  if (router->signing_pkey) {
+    if(crypto_pk_write_public_key_to_string(router->signing_pkey,
+                                         &signing_pkey,&signing_pkeylen)<0) {
+      log(LOG_ERR,"dump_router_to_string(): write signing_pkey to string failed!");
+      return 0;
+    }
+    signing_pkey_tag = "signing-key\n";
+  }
+  
+  result = snprintf(s, maxlen, "router %s %d %d %d %d %d\n%s%s%s",
     router->address,
     router->or_port,
     router->op_port,
     router->ap_port,
     router->dir_port,
     router->bandwidth,
-    pkey);
+    pkey,
+    signing_pkey_tag, signing_pkey);
 
   free(pkey);
+  if (*signing_pkey)
+    free(signing_pkey);
 
   if(result < 0 || result > maxlen) {
     /* apparently different glibcs do different things on snprintf error.. so check both */
@@ -607,21 +622,23 @@ int dump_router_to_string(char *s, int maxlen, routerinfo_t *router) {
 
 }
 
-void dump_directory_to_string(char *s, int maxlen) {
-  int i;
+void dump_directory_to_string(char *s, int maxlen) 
+{
+  directory_t dir;
+  routerinfo_t **routers = NULL;;
   connection_t *conn;
   routerinfo_t *router;
-  int written;
+  int i, n = 0;
 
-  /* first write my own info */
-  if(my_routerinfo) {
-    written = dump_router_to_string(s, maxlen, my_routerinfo);
-    maxlen -= written;
-    s += written;
+  routers = (routerinfo_t**) malloc(sizeof(routerinfo_t*) * (nfds+1));
+  if (!routers) {
+    /* freak out XXX */
+    return;
   }
-
-  /* now write info for other routers */
-  for(i=0;i<nfds;i++) {
+  if (my_routerinfo) {
+    routers[n++] = my_routerinfo;
+  }
+  for(i = 0; i<nfds; ++i) {
     conn = connection_array[i];
 
     if(conn->type != CONN_TYPE_OR)
@@ -633,7 +650,64 @@ void dump_directory_to_string(char *s, int maxlen) {
       log(LOG_ERR,"dump_directory_to_string(): couldn't find router %d:%d!",conn->addr,conn->port);
       continue;
     }
+    routers[n++] = router;
+  }
+  dir.routers = routers;
+  dir.n_routers = n;
+
+  dump_directory_to_string_impl(s, maxlen, &dir);
+}
+
+int
+dump_signed_directory_to_string_impl(char *s, int maxlen, directory_t *dir,
+                                     crypto_pk_env_t *private_key)
+{
+  char *cp;
+  char digest[20];
+  char signature[128];
+  int i;
+  strncpy(s, 
+          "signed-directory\n"
+          "client-software x y z\n" /* XXX make this real */
+          "server-software a b c\n\n" /* XXX make this real */
+          , maxlen);
+  /* These multiple strlen calls are inefficient, but dwarfed by the RSA
+     signature.
+  */
+  i = strlen(s); 
+
+  dump_directory_to_string_impl(s+i, maxlen-i, dir);
+  i = strlen(s);
+  cp = s + i;
+  
+  if (crypto_SHA_digest(s, i, digest))
+    return -1;
+  if (crypto_pk_private_sign(private_key, digest, 20, signature))
+    return -1;
+  
+
+  strncpy(cp, 
+          "directory-signature\n-----BEGIN SIGNATURE-----\n", maxlen-i);
+          
+  i = strlen(s);
+  cp = s+i;
+  if (base64_encode(cp, maxlen-i, signature, 128) < 0)
+    return -1;
 
+  i = strlen(s);
+  cp = s+i;
+  strcat(cp, "-----END SIGNATURE-----\n");
+
+  return 0;
+}
+
+void dump_directory_to_string_impl(char *s, int maxlen, directory_t *directory) {
+  int i;
+  routerinfo_t *router;
+  int written;
+
+  for (i = 0; i < directory->n_routers; ++i) {
+    router = directory->routers[i];
     written = dump_router_to_string(s, maxlen, router);
 
     if(written < 0) { 
@@ -645,7 +719,6 @@ void dump_directory_to_string(char *s, int maxlen) {
     maxlen -= written;
     s += written;
   }
-
 }
 
 void daemonize(void) {

+ 3 - 0
src/or/or.h

@@ -320,6 +320,7 @@ typedef struct {
   uint16_t dir_port;
  
   crypto_pk_env_t *pkey; /* public RSA key */
+  crypto_pk_env_t *signing_pkey; /* May be null */
  
   /* link info */
   uint32_t bandwidth;
@@ -737,6 +738,7 @@ int do_main_loop(void);
 
 void dumpstats(void);
 void dump_directory_to_string(char *s, int maxlen);
+void dump_directory_to_string_impl(char *s, int maxlen, directory_t *directory);
 
 int main(int argc, char *argv[]);
 
@@ -793,6 +795,7 @@ int router_get_list_from_string_impl(char *s, directory_t **dest);
 routerinfo_t *router_get_entry_from_string(char **s);
 
 int router_compare_to_exit_policy(connection_t *conn);
+void routerlist_free(routerinfo_t *list);
 
 #endif
 

+ 36 - 8
src/or/routers.c

@@ -17,7 +17,7 @@ extern routerinfo_t *my_routerinfo; /* from main.c */
 /****************************************************************************/
 
 /* static function prototypes */
-static void routerlist_free(routerinfo_t *list);
+void routerlist_free(routerinfo_t *list);
 static routerinfo_t **make_rarray(routerinfo_t* list, int *len);
 static char *eat_whitespace(char *s);
 static char *find_whitespace(char *s);
@@ -124,7 +124,7 @@ int router_is_me(uint32_t addr, uint16_t port)
 }
 
 /* delete a list of routers from memory */
-static void routerlist_free(routerinfo_t *list)
+void routerlist_free(routerinfo_t *list)
 {
   routerinfo_t *tmp = NULL;
   struct exit_policy_t *e = NULL, *etmp = NULL;
@@ -402,7 +402,6 @@ routerinfo_t *router_get_entry_from_string(char **s) {
     log(LOG_ERR,"router_get_entry_from_string(): Entry does not start with \"router\"");
     return NULL;
   }
-  puts("X");
 
   router = malloc(sizeof(routerinfo_t));
   if (!router) {
@@ -477,7 +476,7 @@ routerinfo_t *router_get_entry_from_string(char **s) {
     goto router_read_failed;
   }
   
-  /* now advance *s so it's at the end of this router entry */
+  /* now advance *s so it's at the end of this public key */
   next = strchr(next, '\n');
   assert(next); /* can't fail, we just checked it was here */
   *next = 0;
@@ -494,10 +493,40 @@ routerinfo_t *router_get_entry_from_string(char **s) {
     goto router_read_failed;
   }
 
-//  test_write_pkey(router->pkey);  
-
   *s = next+1;
-  while(**s != '\n') {
+  *s = eat_whitespace(*s);
+  if (!strncasecmp(*s, "signing-key", 11)) {
+    /* We have a signing key */
+    *s = strchr(*s, '\n');
+    *s = eat_whitespace(*s); 
+    next = strstr(*s,OR_PUBLICKEY_END_TAG);
+    router->signing_pkey = crypto_new_pk_env(CRYPTO_PK_RSA);
+    if (!next || !router->signing_pkey) {
+      log(LOG_ERR,"router_get_entry_from_string(): Couldn't find signing_pk in string");
+      goto router_read_failed;
+    }
+    next = strchr(next, '\n');
+    assert(next);
+    *next = 0;
+    if ((crypto_pk_read_public_key_from_string(router->signing_pkey, *s,
+                                               strlen(*s)))<0) {
+      log(LOG_ERR,"router_get_entry_from_string(): Couldn't read signing pk from string");
+      goto router_read_failed;
+    }
+
+    log(LOG_DEBUG,"router_get_entry_from_string(): Signing key size = %u.", crypto_pk_keysize(router->signing_pkey));
+
+    if (crypto_pk_keysize(router->signing_pkey) != 128) { /* keys MUST be 1024 bits in size */
+      log(LOG_ERR,"Signing key for router %s:%u is 1024 bits. All keys must be exactly 1024 bits long.",
+          router->address,router->or_port);
+      goto router_read_failed;
+    }
+    *s = next+1;
+  }
+      
+  //  test_write_pkey(router->pkey);  
+
+  while(**s && **s != '\n') {
     /* pull in a line of exit policy */
     next = strchr(*s, '\n');
     if(!next)
@@ -509,7 +538,6 @@ routerinfo_t *router_get_entry_from_string(char **s) {
 
   return router;
 
-
 router_read_failed:
   if(router->address)
     free(router->address);

+ 19 - 5
src/or/test.c

@@ -506,8 +506,8 @@ void
 test_dir_format()
 {
   
-  char buf[2048], buf2[512];
-  char *pk1_str, *pk2_str, *cp;
+  char buf[2048], buf2[2048];
+  char *pk1_str = NULL, *pk2_str = NULL, *cp;
   int pk1_str_len, pk2_str_len;
   routerinfo_t r1, r2;
   crypto_pk_env_t *pk1 = NULL, *pk2 = NULL;
@@ -528,6 +528,7 @@ test_dir_format()
   r1.ap_port = 9002;
   r1.dir_port = 9003;
   r1.pkey = pk1;
+  r1.signing_pkey = NULL;
   r1.bandwidth = 1000;
   r1.exit_policy = NULL;
   r1.next = &r2;
@@ -548,6 +549,7 @@ test_dir_format()
   r2.ap_port = 0;
   r2.dir_port = 0;
   r2.pkey = pk2;
+  r2.signing_pkey = pk1;
   r2.bandwidth = 3000;
   r2.exit_policy = &ex1;
   r2.next = NULL;
@@ -574,10 +576,13 @@ test_dir_format()
   test_eq(rp1->dir_port, r1.dir_port);
   test_eq(rp1->bandwidth, r1.bandwidth);
   test_assert(crypto_pk_cmp_keys(rp1->pkey, pk1) == 0);
+  test_assert(rp1->signing_pkey == NULL);
   test_assert(rp1->exit_policy == NULL);
 
   strcpy(buf2, "router tor.tor.tor 9005 0 0 0 3000\n");
   strcat(buf2, pk2_str);
+  strcat(buf2, "signing-key\n");
+  strcat(buf2, pk1_str);
   strcat(buf2, "accept *:80\nreject 18.*:24\n\n");
   test_assert(dump_router_to_string(buf, 2048, &r2)>0);
   test_streq(buf, buf2);
@@ -592,6 +597,7 @@ test_dir_format()
   test_eq(rp2->dir_port, r2.dir_port);
   test_eq(rp2->bandwidth, r2.bandwidth);
   test_assert(crypto_pk_cmp_keys(rp2->pkey, pk2) == 0);
+  test_assert(crypto_pk_cmp_keys(rp2->signing_pkey, pk1) == 0);
   test_eq(rp2->exit_policy->policy_type, EXIT_POLICY_ACCEPT);
   test_streq(rp2->exit_policy->string, "accept *:80");
   test_streq(rp2->exit_policy->address, "*");
@@ -601,9 +607,17 @@ test_dir_format()
   test_streq(rp2->exit_policy->next->address, "18.*");
   test_streq(rp2->exit_policy->next->port, "24");
   test_assert(rp2->exit_policy->next->next == NULL);
+
+  /* Okay, now for the directories. */
   
-  
-  /* XXXX free everything*/
+
+
+  if (pk1_str) free(pk1_str);
+  if (pk2_str) free(pk2_str);
+  if (pk1) crypto_free_pk_env(pk1);
+  if (pk2) crypto_free_pk_env(pk2);
+  if (rp1) routerlist_free(rp1);
+  if (rp2) routerlist_free(rp2);
 }
 
 int 
@@ -617,7 +631,7 @@ main(int c, char**v) {
   log(LOG_ERR,NULL);         /* make logging quieter */
 
   setup_directory();
-#if 1
+#if 0
   puts("========================== Buffers =========================");
   test_buffers();
   puts("========================== Crypto ==========================");