Browse Source

Make base-64-encoded DER work, including workaround for ugly openssl misfeature that makes base64 decoding fail when you strip out the newlines.

svn:r2423
Nick Mathewson 21 years ago
parent
commit
ce3162d035
5 changed files with 72 additions and 33 deletions
  1. 43 24
      src/common/crypto.c
  2. 3 0
      src/common/crypto.h
  3. 1 0
      src/common/util.h
  4. 10 8
      src/or/dirserv.c
  5. 15 1
      src/or/test.c

+ 43 - 24
src/common/crypto.c

@@ -463,48 +463,67 @@ crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env,
   return r;
 }
 
-int crypto_pk_DER64_encode_key(crypto_pk_env_t *env, char **out)
+/** Allocate a new string in *<b>out</b>, containing the public portion of the
+ * RSA key in <b>env</b>, encoded first with DER, then in base-64.  Return the
+ * length of the encoded representation on success, and -1 on failure.
+ *
+ * <i>This function is for temporary use only.  We need a simple
+ * one-line representation for keys to work around a bug in parsing
+ * directories containing "opt keyword\n-----BEGIN OBJECT----" entries
+ * in versions of Tor up to 0.0.9pre2.</i>
+ */
+int crypto_pk_DER64_encode_public_key(crypto_pk_env_t *env, char **out)
 {
   int len;
-  char *s, *sp;
+  char buf[PK_BYTES*2]; /* Too long, but hey, stacks are big. */
   tor_assert(env && out);
-  len = i2d_RSAPublicKey(env->key, NULL);
+  len = crypto_pk_asn1_encode(env, buf, sizeof(buf));
   if (len < 0) {
     return -1;
   }
-  s = sp = tor_malloc(len+1);
-  i2d_RSAPublicKey(env->key, &sp); /* modifies sp */
   *out = tor_malloc(len * 2); /* too long, but safe. */
-  if (base64_encode(*out, len*2, s, len) < 0) {
+  if (base64_encode(*out, len*2, buf, len) < 0) {
     log_fn(LOG_WARN, "Error base64-encoding DER-encoded key");
     tor_free(*out);
-    tor_free(s);
     return -1;
   }
-  tor_free(s);
-  return len;
+  /* Remove spaces */
+  tor_strstrip(*out, " \r\n\t");
+  return strlen(*out);
 }
 
-int crypto_pk_DER64_decode_key(crypto_pk_env_t *env, const char *in)
+/** Decode a base-64 encoded DER representation of an RSA key from <b>in</b>,
+ * and store the result in <b>env</b>.  Return 0 on success, -1 on failure.
+ *
+ * <i>This function is for temporary use only.  We need a simple
+ * one-line representation for keys to work around a bug in parsing
+ * directories containing "opt keyword\n-----BEGIN OBJECT----" entries
+ * in versions of Tor up to 0.0.9pre2.</i>
+ */
+crypto_pk_env_t *crypto_pk_DER64_decode_public_key(const char *in)
 {
-  char *buf, *bufp;
-  RSA *rsa;
+  char buf1[PK_BYTES*2 + PK_BYTES/64 + 2];
+  char buf[PK_BYTES*2];
   int len;
-  tor_assert(env && in);
+  int i;
+  tor_assert(in);
   len = strlen(in);
-  buf = bufp = tor_malloc(len+1);
-  if (base64_decode(buf, len+1, in, len)<0) {
-    tor_free(buf);
+
+  if (strlen(in) > PK_BYTES*2) {
+    return NULL;
+  }
+  /* base64_decode doesn't work unless we insert linebreaks every 64
+   * characters.  how dumb. */
+  for(i=0;i*64<len;i+=1) {
+    strncpy(buf1+(64+1)*i, in+64*i, 64);
+    strcpy(buf1+(64+1)*i + 64, "\n");
+  }
+  len = base64_decode(buf, sizeof(buf), buf1, strlen(buf1));
+  if (len<0) {
     log_fn(LOG_WARN,"Error base-64 decoding key");
-    return -1;
+    return NULL;
   }
-  rsa = d2i_RSAPublicKey(NULL, &bufp, strlen(buf));
-  tor_free(buf);
-  if (!rsa)
-    return -1;
-  if (env->key) RSA_free(env->key);
-  env->key = rsa;
-  return 0;
+  return crypto_pk_asn1_decode(buf, len);
 }
 
 /** Return true iff <b>env</b> has a valid key.

+ 3 - 0
src/common/crypto.h

@@ -64,6 +64,9 @@ int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src,
 int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, const char *fname);
 int crypto_pk_check_key(crypto_pk_env_t *env);
 int crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, const char *keyfile);
+int crypto_pk_DER64_encode_public_key(crypto_pk_env_t *env, char **dest);
+crypto_pk_env_t *crypto_pk_DER64_decode_public_key(const char *in);
+
 
 int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b);
 crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *orig);

+ 1 - 0
src/common/util.h

@@ -87,6 +87,7 @@ char *tor_strndup(const char *s, size_t n);
 #define tor_free(p) do {if(p) {free(p); (p)=NULL;}} while(0)
 void tor_strlower(char *s);
 int strcmpstart(const char *s1, const char *s2);
+int tor_strstrip(char *s, const char *strip);
 
 /* Some platforms segfault when you try to access a multi-byte type
  * that isn't aligned to a word boundary.  The macros and/or functions

+ 10 - 8
src/or/dirserv.c

@@ -557,7 +557,7 @@ dirserv_dump_directory_to_string(char *s, unsigned int maxlen,
   char signature[128];
   char published[33];
   time_t published_on;
-  int i, identity_pkeylen;
+  int i;
   eos = s+maxlen;
 
   if (!descriptor_list)
@@ -565,14 +565,15 @@ dirserv_dump_directory_to_string(char *s, unsigned int maxlen,
 
   if (list_running_servers(&cp))
     return -1;
-#if 0
-  /* PEM-encode the identity key key */
-  if(crypto_pk_write_public_key_to_string(private_key,
-                                        &identity_pkey,&identity_pkeylen)<0) {
+
+  /* ASN.1-encode the public key.  This is a temporary measure; once
+   * everyone is running 0.0.9pre3 or later, we can shift to using a
+   * PEM-encoded key instead.
+   */
+  if(crypto_pk_DER64_encode_public_key(private_key, &identity_pkey)<0) {
     log_fn(LOG_WARN,"write identity_pkey to string failed!");
     return -1;
   }
-#endif
   dirserv_remove_old_servers(ROUTER_MAX_AGE);
   published_on = time(NULL);
   format_iso_time(published, published_on);
@@ -580,8 +581,9 @@ dirserv_dump_directory_to_string(char *s, unsigned int maxlen,
            "signed-directory\n"
            "published %s\n"
            "recommended-software %s\n"
-           "running-routers %s\n\n",
-           published, options.RecommendedVersions, cp);
+           "running-routers %s\n"
+           "opt dir-signing-key %s\n\n",
+           published, options.RecommendedVersions, cp, identity_pkey);
 
   tor_free(cp);
   tor_free(identity_pkey);

+ 15 - 1
src/or/test.c

@@ -365,6 +365,20 @@ test_crypto()
   test_assert(! crypto_pk_write_public_key_to_string(pk1, &cp, &i));
   test_assert(! crypto_pk_read_public_key_from_string(pk2, cp, i));
   test_eq(0, crypto_pk_cmp_keys(pk1, pk2));
+  tor_free(cp);
+
+  /* Check DER encoding */
+  i=crypto_pk_DER64_encode_public_key(pk1, &cp);
+  test_assert(i>0);
+  test_assert(cp);
+  test_assert(!strchr(cp, ' '));
+  test_assert(!strchr(cp, '\n'));
+  test_eq(0, crypto_pk_cmp_keys(pk1, pk1));
+  crypto_free_pk_env(pk2);
+  pk2 = crypto_pk_DER64_decode_public_key(cp);
+  test_assert(pk2);
+  test_eq(0, crypto_pk_cmp_keys(pk1, pk2));
+  tor_free(cp);
 
   test_eq(128, crypto_pk_keysize(pk1));
   test_eq(128, crypto_pk_keysize(pk2));
@@ -1095,6 +1109,7 @@ main(int c, char**v){
 //  puts("========================== Buffers =========================");
 //  test_buffers();
   puts("\n========================== Crypto ==========================");
+  // add_stream_log(LOG_DEBUG, LOG_ERR, "<stdout>", stdout);
   test_crypto();
   test_crypto_dh();
   puts("\n========================= Util ============================");
@@ -1105,7 +1120,6 @@ main(int c, char**v){
   test_onion();
   test_onion_handshake();
   puts("\n========================= Directory Formats ===============");
-  /* add_stream_log(LOG_DEBUG, LOG_ERR, "<stdout>", stdout); */
   test_dir_format();
   puts("\n========================= Rendezvous functionality ========");
   test_rend_fns();