Browse Source

Implement ed25519-signed descriptors

Now that we have ed25519 keys, we can sign descriptors with them
and check those signatures as documented in proposal 220.
Nick Mathewson 11 years ago
parent
commit
fe5d2477aa

+ 81 - 1
src/common/crypto.c

@@ -1724,7 +1724,24 @@ crypto_digest_assign(crypto_digest_t *into,
  * <b>out_len</b> must be \<= DIGEST256_LEN. */
 void
 crypto_digest_smartlist(char *digest_out, size_t len_out,
-                        const smartlist_t *lst, const char *append,
+                        const smartlist_t *lst,
+                        const char *append,
+                        digest_algorithm_t alg)
+{
+  crypto_digest_smartlist_prefix(digest_out, len_out, NULL, lst, append, alg);
+}
+
+/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
+ * at <b>digest_out</b> to the hash of the concatenation of: the
+ * optional string <b>prepend</b>, those strings,
+ * and the optional string <b>append</b>, computed with the algorithm
+ * <b>alg</b>.
+ * <b>out_len</b> must be \<= DIGEST256_LEN. */
+void
+crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
+                        const char *prepend,
+                        const smartlist_t *lst,
+                        const char *append,
                         digest_algorithm_t alg)
 {
   crypto_digest_t *d;
@@ -1732,6 +1749,8 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
     d = crypto_digest_new();
   else
     d = crypto_digest256_new(alg);
+  if (prepend)
+    crypto_digest_add_bytes(d, prepend, strlen(prepend));
   SMARTLIST_FOREACH(lst, const char *, cp,
                     crypto_digest_add_bytes(d, cp, strlen(cp)));
   if (append)
@@ -2692,6 +2711,8 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen)
     return -1;
   if (destlen > SIZE_T_CEILING)
     return -1;
+  if (destlen)
+    *dest = 0; /* Ensure we always initialize the buffer */
 
   EVP_EncodeInit(&ctx);
   EVP_EncodeUpdate(&ctx, (unsigned char*)dest, &len,
@@ -2701,6 +2722,65 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen)
   return ret;
 }
 
+/** As base64_encode, but do not add any internal spaces or external padding
+ * to the output stream. */
+int
+base64_encode_nopad(char *dest, size_t destlen,
+                    const uint8_t *src, size_t srclen)
+{
+  int n = base64_encode(dest, destlen, (const char*) src, srclen);
+  if (n <= 0)
+    return n;
+  tor_assert((size_t)n < destlen && dest[n] == 0);
+  char *in, *out;
+  in = out = dest;
+  while (*in) {
+    if (*in == '=' || *in == '\n') {
+      ++in;
+    } else {
+      *out++ = *in++;
+    }
+  }
+  *out = 0;
+
+  tor_assert(out - dest <= INT_MAX);
+
+  return (int)(out - dest);
+}
+
+/** As base64_decode, but do not require any padding on the input */
+int
+base64_decode_nopad(uint8_t *dest, size_t destlen,
+                    const char *src, size_t srclen)
+{
+  if (srclen > SIZE_T_CEILING - 4)
+    return -1;
+  char *buf = tor_malloc(srclen + 4);
+  memcpy(buf, src, srclen+1);
+  size_t buflen;
+  switch (srclen % 4)
+    {
+    case 0:
+    default:
+      buflen = srclen;
+      break;
+    case 1:
+      tor_free(buf);
+      return -1;
+    case 2:
+      memcpy(buf+srclen, "==", 3);
+      buflen = srclen + 2;
+      break;
+    case 3:
+      memcpy(buf+srclen, "=", 2);
+      buflen = srclen + 1;
+      break;
+  }
+  int n = base64_decode((char*)dest, destlen, buf, buflen);
+  tor_free(buf);
+  return n;
+}
+
 /** @{ */
 /** Special values used for the base64_decode_table */
 #define X 255

+ 10 - 0
src/common/crypto.h

@@ -207,6 +207,11 @@ int crypto_digest256(char *digest, const char *m, size_t len,
                      digest_algorithm_t algorithm);
 int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
 struct smartlist_t;
+void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
+                                    const char *prepend,
+                                    const struct smartlist_t *lst,
+                                    const char *append,
+                                    digest_algorithm_t alg);
 void crypto_digest_smartlist(char *digest_out, size_t len_out,
                              const struct smartlist_t *lst, const char *append,
                              digest_algorithm_t alg);
@@ -270,6 +275,11 @@ void smartlist_shuffle(struct smartlist_t *sl);
 
 int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen);
 int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen);
+int base64_encode_nopad(char *dest, size_t destlen,
+                        const uint8_t *src, size_t srclen);
+int base64_decode_nopad(uint8_t *dest, size_t destlen,
+                        const char *src, size_t srclen);
+
 /** Characters that can appear (case-insensitively) in a base32 encoding. */
 #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
 void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);

+ 11 - 0
src/common/crypto_ed25519.c

@@ -351,6 +351,7 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
   return 0;
 }
 
+/** Release all storage held for <b>kp</b>. */
 void
 ed25519_keypair_free(ed25519_keypair_t *kp)
 {
@@ -361,3 +362,13 @@ ed25519_keypair_free(ed25519_keypair_t *kp)
   tor_free(kp);
 }
 
+/** Return true iff <b>key1</b> and <b>key2</b> are the same public key. */
+int
+ed25519_pubkey_eq(const ed25519_public_key_t *key1,
+                  const ed25519_public_key_t *key2)
+{
+  tor_assert(key1);
+  tor_assert(key2);
+  return tor_memeq(key1->pubkey, key2->pubkey, ED25519_PUBKEY_LEN);
+}
+

+ 10 - 0
src/common/crypto_ed25519.h

@@ -95,6 +95,13 @@ int ed25519_public_from_base64(ed25519_public_key_t *pkey,
 int ed25519_public_to_base64(char *output,
                              const ed25519_public_key_t *pkey);
 
+#define ED25519_SIG_BASE64_LEN 86
+
+int ed25519_signature_from_base64(ed25519_signature_t *sig,
+                                  const char *input);
+int ed25519_signature_to_base64(char *output,
+                                const ed25519_signature_t *sig);
+
 /* XXXX read encrypted, write encrypted. */
 
 int ed25519_seckey_write_to_file(const ed25519_secret_key_t *seckey,
@@ -112,5 +119,8 @@ int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
 
 void ed25519_keypair_free(ed25519_keypair_t *kp);
 
+int ed25519_pubkey_eq(const ed25519_public_key_t *key1,
+                      const ed25519_public_key_t *key2);
+
 #endif
 

+ 39 - 0
src/common/crypto_format.c

@@ -65,3 +65,42 @@ ed25519_public_to_base64(char *output,
   return digest256_to_base64(output, (const char *)pkey->pubkey);
 }
 
+/** Encode the signature <b>sig</b> into the buffer at <b>output</b>,
+ * which must have space for ED25519_SIG_BASE64_LEN bytes of encoded signature,
+ * plus one byte for a terminating NUL.  Return 0 on success, -1 on failure.
+ */
+int
+ed25519_signature_to_base64(char *output,
+                            const ed25519_signature_t *sig)
+{
+  char buf[256];
+  int n = base64_encode_nopad(buf, sizeof(buf), sig->sig, ED25519_SIG_LEN);
+  tor_assert(n == ED25519_SIG_BASE64_LEN);
+  memcpy(output, buf, ED25519_SIG_BASE64_LEN+1);
+  return 0;
+}
+
+/** Try to decode the string <b>input</b> into an ed25519 signature. On
+ * success, store the value in <b>sig</b> and return 0. Otherwise return
+ * -1. */
+int
+ed25519_signature_from_base64(ed25519_signature_t *sig,
+                              const char *input)
+{
+
+  if (strlen(input) != ED25519_SIG_BASE64_LEN)
+    return -1;
+  char buf[ED25519_SIG_BASE64_LEN+3];
+  memcpy(buf, input, ED25519_SIG_BASE64_LEN);
+  buf[ED25519_SIG_BASE64_LEN+0] = '=';
+  buf[ED25519_SIG_BASE64_LEN+1] = '=';
+  buf[ED25519_SIG_BASE64_LEN+2] = 0;
+  char decoded[128];
+  int n = base64_decode(decoded, sizeof(decoded), buf, strlen(buf));
+  if (n < 0 || n != ED25519_SIG_LEN)
+    return -1;
+  memcpy(sig->sig, decoded, ED25519_SIG_LEN);
+
+  return 0;
+}
+

+ 2 - 0
src/or/or.h

@@ -2021,6 +2021,8 @@ typedef struct {
   crypto_pk_t *identity_pkey;  /**< Public RSA key for signing. */
   /** Public curve25519 key for onions */
   curve25519_public_key_t *onion_curve25519_pkey;
+  /** Certificate for ed25519 signing key */
+  struct tor_cert_st *signing_key_cert;
 
   char *platform; /**< What software/operating system is this OR using? */
 

+ 56 - 5
src/or/router.c

@@ -30,6 +30,7 @@
 #include "routerlist.h"
 #include "routerparse.h"
 #include "statefile.h"
+#include "torcert.h"
 #include "transports.h"
 #include "routerset.h"
 
@@ -1881,6 +1882,8 @@ router_rebuild_descriptor(int force)
     routerinfo_free(ri);
     return -1;
   }
+  ri->signing_key_cert = tor_cert_dup(get_master_signing_key_cert());
+
   get_platform_str(platform, sizeof(platform));
   ri->platform = tor_strdup(platform);
 
@@ -1995,8 +1998,9 @@ router_rebuild_descriptor(int force)
     /* ri was allocated with tor_malloc_zero, so there is no need to
      * zero ri->cache_info.extra_info_digest here. */
   }
-  if (! (ri->cache_info.signed_descriptor_body = router_dump_router_to_string(
-                                           ri, get_server_identity_key()))) {
+  if (! (ri->cache_info.signed_descriptor_body =
+          router_dump_router_to_string(ri, get_server_identity_key(),
+                                       get_master_signing_keypair())) ) {
     log_warn(LD_BUG, "Couldn't generate router descriptor.");
     routerinfo_free(ri);
     extrainfo_free(ei);
@@ -2302,12 +2306,13 @@ get_platform_str(char *platform, size_t len)
  */
 char *
 router_dump_router_to_string(routerinfo_t *router,
-                             crypto_pk_t *ident_key)
+                             crypto_pk_t *ident_key,
+                             const ed25519_keypair_t *signing_keypair)
 {
   char *address = NULL;
   char *onion_pkey = NULL; /* Onion key, PEM-encoded. */
   char *identity_pkey = NULL; /* Identity key, PEM-encoded. */
-  char digest[DIGEST_LEN];
+  char digest[DIGEST256_LEN];
   char published[ISO_TIME_LEN+1];
   char fingerprint[FINGERPRINT_LEN+1];
   int has_extra_info_digest;
@@ -2318,6 +2323,8 @@ router_dump_router_to_string(routerinfo_t *router,
   const or_options_t *options = get_options();
   smartlist_t *chunks = NULL;
   char *output = NULL;
+  const int emit_ed_sigs = signing_keypair && router->signing_key_cert;
+  char *ed_cert_line = NULL;
 
   /* Make sure the identity key matches the one in the routerinfo. */
   if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) {
@@ -2325,6 +2332,15 @@ router_dump_router_to_string(routerinfo_t *router,
              "match router's public key!");
     goto err;
   }
+  if (emit_ed_sigs) {
+    if (!router->signing_key_cert->signing_key_included ||
+        !ed25519_pubkey_eq(&router->signing_key_cert->signed_key,
+                           &signing_keypair->pubkey)) {
+      log_warn(LD_BUG, "Tried to sign a router descriptor with a mismatched "
+               "ed25519 key chain");
+      goto err;
+    }
+  }
 
   /* record our fingerprint, so we can include it in the descriptor */
   if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint, 1)<0) {
@@ -2332,6 +2348,21 @@ router_dump_router_to_string(routerinfo_t *router,
     goto err;
   }
 
+  if (emit_ed_sigs) {
+    /* Encode ed25519 signing cert */
+    char ed_cert_base64[256];
+    if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64),
+                      (const char*)router->signing_key_cert->encoded,
+                      router->signing_key_cert->encoded_len) < 0) {
+      log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
+      goto err;
+    }
+    tor_asprintf(&ed_cert_line, "identity-ed25519\n"
+                 "-----BEGIN ED25519 CERT-----\n"
+                 "%s"
+                 "-----END ED25519 CERT-----\n", ed_cert_base64);
+  }
+
   /* PEM-encode the onion key */
   if (crypto_pk_write_public_key_to_string(router->onion_pkey,
                                            &onion_pkey,&onion_pkeylen)<0) {
@@ -2385,6 +2416,7 @@ router_dump_router_to_string(routerinfo_t *router,
   smartlist_add_asprintf(chunks,
                     "router %s %s %d 0 %d\n"
                     "%s"
+                    "%s"
                     "platform %s\n"
                     "protocols Link 1 2 Circuit 1\n"
                     "published %s\n"
@@ -2399,6 +2431,7 @@ router_dump_router_to_string(routerinfo_t *router,
     address,
     router->or_port,
     decide_to_advertise_dirport(options, router->dir_port),
+    ed_cert_line ? ed_cert_line : "",
     extra_or_address ? extra_or_address : "",
     router->platform,
     published,
@@ -2455,7 +2488,24 @@ router_dump_router_to_string(routerinfo_t *router,
     tor_free(p6);
   }
 
-  /* Sign the descriptor */
+  /* Sign the descriptor with Ed25519 */
+  if (emit_ed_sigs)  {
+    smartlist_add(chunks, tor_strdup("router-sig-ed25519 "));
+    crypto_digest_smartlist_prefix(digest, DIGEST256_LEN,
+                                   ED_DESC_SIGNATURE_PREFIX,
+                                   chunks, "", DIGEST_SHA256);
+    ed25519_signature_t sig;
+    char buf[ED25519_SIG_BASE64_LEN+1];
+    if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN,
+                     signing_keypair) < 0)
+      goto err;
+    if (ed25519_signature_to_base64(buf, &sig) < 0)
+      goto err;
+
+    smartlist_add_asprintf(chunks, "%s\n", buf);
+  }
+
+  /* Sign the descriptor with RSA */
   smartlist_add(chunks, tor_strdup("router-signature\n"));
 
   crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1);
@@ -2507,6 +2557,7 @@ router_dump_router_to_string(routerinfo_t *router,
   tor_free(onion_pkey);
   tor_free(identity_pkey);
   tor_free(extra_or_address);
+  tor_free(ed_cert_line);
 
   return output;
 }

+ 2 - 1
src/or/router.h

@@ -91,7 +91,8 @@ int router_is_me(const routerinfo_t *router);
 int router_pick_published_address(const or_options_t *options, uint32_t *addr);
 int router_rebuild_descriptor(int force);
 char *router_dump_router_to_string(routerinfo_t *router,
-                                   crypto_pk_t *ident_key);
+                                   crypto_pk_t *ident_key,
+                                   const ed25519_keypair_t *signing_keypair);
 char *router_dump_exit_policy_to_string(const routerinfo_t *router,
                                          int include_ipv4,
                                          int include_ipv6);

+ 1 - 1
src/or/routerkeys.c

@@ -300,7 +300,7 @@ load_ed_keys(const or_options_t *options, time_t now)
   (void) options;
 
   id = ed_key_init_from_file(
-               options_get_datadir_fname2(options, "keys", "ed25519_master_id"),
+             options_get_datadir_fname2(options, "keys", "ed25519_master_id"),
                              (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT|
                               INIT_ED_KEY_MISSING_SECRET_OK|
                               INIT_ED_KEY_EXTRA_STRONG),

+ 3 - 0
src/or/routerlist.c

@@ -13,6 +13,7 @@
 
 #define ROUTERLIST_PRIVATE
 #include "or.h"
+#include "crypto_ed25519.h"
 #include "circuitstats.h"
 #include "config.h"
 #include "connection.h"
@@ -38,6 +39,7 @@
 #include "routerparse.h"
 #include "routerset.h"
 #include "../common/sandbox.h"
+#include "torcert.h"
 // #define DEBUG_ROUTERLIST
 
 /****************************************************************************/
@@ -2663,6 +2665,7 @@ routerinfo_free(routerinfo_t *router)
   tor_free(router->onion_curve25519_pkey);
   if (router->identity_pkey)
     crypto_pk_free(router->identity_pkey);
+  tor_cert_free(router->signing_key_cert);
   if (router->declared_family) {
     SMARTLIST_FOREACH(router->declared_family, char *, s, tor_free(s));
     smartlist_free(router->declared_family);

+ 91 - 0
src/or/routerparse.c

@@ -26,6 +26,8 @@
 #include "rephist.h"
 #include "routerparse.h"
 #include "entrynodes.h"
+#include "torcert.h"
+
 #undef log
 #include <math.h>
 
@@ -83,6 +85,8 @@ typedef enum {
   K_HIDDEN_SERVICE_DIR,
   K_ALLOW_SINGLE_HOP_EXITS,
   K_IPV6_POLICY,
+  K_ROUTER_SIG_ED25519,
+  K_IDENTITY_ED25519,
 
   K_DIRREQ_END,
   K_DIRREQ_V2_IPS,
@@ -293,6 +297,9 @@ static token_rule_t routerdesc_token_table[] = {
   T01("write-history",       K_WRITE_HISTORY,       ARGS,    NO_OBJ ),
   T01("extra-info-digest",   K_EXTRA_INFO_DIGEST,   GE(1),   NO_OBJ ),
   T01("hidden-service-dir",  K_HIDDEN_SERVICE_DIR,  NO_ARGS, NO_OBJ ),
+  T01("identity-ed25519",    K_IDENTITY_ED25519,    NO_ARGS, NEED_OBJ ),
+  T01("router-sig-ed25519",  K_ROUTER_SIG_ED25519,  GE(1),   NO_OBJ ),
+
   T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS,    NO_ARGS, NO_OBJ ),
 
   T01("family",              K_FAMILY,              ARGS,    NO_OBJ ),
@@ -506,6 +513,10 @@ static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
                                                unsigned fmt_flags);
 static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
 
+static int router_get_hash_impl_helper(const char *s, size_t s_len,
+                            const char *start_str,
+                            const char *end_str, char end_c,
+                            const char **start_out, const char **end_out);
 static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
                                 const char *start_str, const char *end_str,
                                 char end_char,
@@ -1302,6 +1313,86 @@ router_parse_entry_from_string(const char *s, const char *end,
       tor_memdup(&k, sizeof(curve25519_public_key_t));
   }
 
+  {
+    directory_token_t *ed_sig_tok, *ed_cert_tok;
+    ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519);
+    ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519);
+    if (!ed_sig_tok != !ed_cert_tok) {
+      log_warn(LD_DIR, "Router descriptor with only partial ed25519 support");
+      goto err;
+    }
+    if (ed_sig_tok) {
+      tor_assert(ed_cert_tok);
+      if (ed_cert_tok != smartlist_get(tokens, 0) &&
+          ed_cert_tok != smartlist_get(tokens, 1)) {
+        log_warn(LD_DIR, "Ed25519 certificate in wrong position");
+        goto err;
+      }
+      if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) {
+        log_warn(LD_DIR, "Ed25519 signature in wrong position");
+        goto err;
+      }
+      if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) {
+        log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor");
+        goto err;
+      }
+
+      uint8_t d256[DIGEST256_LEN];
+      const char *signed_start, *signed_end;
+
+      tor_cert_t *cert = tor_cert_parse(
+                       (const uint8_t*)ed_cert_tok->object_body,
+                       ed_cert_tok->object_size);
+      if (! cert) {
+        log_warn(LD_DIR, "Couldn't parse ed25519 cert");
+        goto err;
+      }
+      router->signing_key_cert = cert;
+      if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
+          ! cert->signing_key_included) {
+        log_warn(LD_DIR, "Invalid form for ed25519 cert");
+        goto err;
+      }
+
+      if (router_get_hash_impl_helper(s, end-s, "router ",
+                                      "\nrouter-sig-ed25519",
+                                      ' ', &signed_start, &signed_end) < 0) {
+        log_warn(LD_DIR, "Can't find ed25519-signed portion of descriptor");
+        goto err;
+      }
+      crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256);
+      crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX,
+        strlen(ED_DESC_SIGNATURE_PREFIX));
+      crypto_digest_add_bytes(d, signed_start, signed_end-signed_start);
+      crypto_digest_get_digest(d, (char*)d256, sizeof(d256));
+      crypto_digest_free(d);
+
+      ed25519_checkable_t check[2];
+      int check_ok[2];
+      if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) {
+        log_err(LD_BUG, "Couldn't create 'checkable' for cert.");
+        goto err;
+      }
+      if (ed25519_signature_from_base64(&check[1].signature,
+                                        ed_sig_tok->args[0])<0) {
+        log_warn(LD_DIR, "Couldn't decode ed25519 signature");
+        goto err;
+      }
+      check[1].pubkey = &cert->signed_key;
+      check[1].msg = d256;
+      check[1].len = DIGEST256_LEN;
+
+      if (ed25519_checksig_batch(check_ok, check, 2) < 0) {
+        log_warn(LD_DIR, "Incorrect ed25519 signatures");
+        goto err;
+      }
+      if (cert->valid_until < time(NULL)) {
+        log_warn(LD_DIR, "Expired ed25519 certificate in router descriptor");
+        goto err;
+      }
+    }
+  }
+
   tok = find_by_keyword(tokens, K_SIGNING_KEY);
   router->identity_pkey = tok->key;
   tok->key = NULL; /* Prevent free */

+ 2 - 0
src/or/routerparse.h

@@ -92,5 +92,7 @@ STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str,
                                             routerstatus_t *rs);
 #endif
 
+#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1"
+
 #endif
 

+ 10 - 0
src/or/torcert.c

@@ -206,3 +206,13 @@ tor_cert_checksig(tor_cert_t *cert,
   }
 }
 
+/** Return a new copy of <b>cert</b> */
+tor_cert_t *
+tor_cert_dup(const tor_cert_t *cert)
+{
+  tor_cert_t *newcert = tor_memdup(cert, sizeof(tor_cert_t));
+  if (cert->encoded)
+    newcert->encoded = tor_memdup(cert->encoded, cert->encoded_len);
+  return newcert;
+}
+

+ 2 - 0
src/or/torcert.h

@@ -62,5 +62,7 @@ int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out,
 int tor_cert_checksig(tor_cert_t *cert,
                       const ed25519_public_key_t *pubkey, time_t now);
 
+tor_cert_t *tor_cert_dup(const tor_cert_t *cert);
+
 #endif
 

+ 19 - 1
src/test/test_crypto.c

@@ -620,9 +620,18 @@ test_crypto_formats(void *arg)
   for (idx = 0; idx < 10; ++idx) {
     i = base64_encode(data2, 1024, data1, idx);
     tt_int_op(i, OP_GE, 0);
+    tt_int_op(i, OP_EQ, strlen(data2));
     j = base64_decode(data3, 1024, data2, i);
     tt_int_op(j,OP_EQ, idx);
     tt_mem_op(data3,OP_EQ, data1, idx);
+
+    i = base64_encode_nopad(data2, 1024, (uint8_t*)data1, idx);
+    tt_int_op(i, OP_GE, 0);
+    tt_int_op(i, OP_EQ, strlen(data2));
+    tt_assert(! strchr(data2, '='));
+    j = base64_decode_nopad((uint8_t*)data3, 1024, data2, i);
+    tt_int_op(j, OP_EQ, idx);
+    tt_mem_op(data3,OP_EQ, data1, idx);
   }
 
   strlcpy(data1, "Test string that contains 35 chars.", 1024);
@@ -1172,6 +1181,8 @@ test_crypto_ed25519_simple(void *arg)
   tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub2, &sec1));
 
   tt_mem_op(pub1.pubkey, OP_EQ, pub2.pubkey, sizeof(pub1.pubkey));
+  tt_assert(ed25519_pubkey_eq(&pub1, &pub2));
+  tt_assert(ed25519_pubkey_eq(&pub1, &pub1));
 
   memcpy(&kp1.pubkey, &pub1, sizeof(pub1));
   memcpy(&kp1.seckey, &sec1, sizeof(sec1));
@@ -1191,6 +1202,7 @@ test_crypto_ed25519_simple(void *arg)
   /* Wrong public key doesn't work. */
   tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub2, &sec2));
   tt_int_op(-1, OP_EQ, ed25519_checksig(&sig2, msg, msg_len, &pub2));
+  tt_assert(! ed25519_pubkey_eq(&pub1, &pub2));
 
   /* Wrong message doesn't work. */
   tt_int_op(0, OP_EQ, ed25519_checksig(&sig2, msg, msg_len, &pub1));
@@ -1329,9 +1341,10 @@ test_crypto_ed25519_test_vectors(void *arg)
 static void
 test_crypto_ed25519_encode(void *arg)
 {
-  char buf[ED25519_BASE64_LEN+1];
+  char buf[ED25519_SIG_BASE64_LEN+1];
   ed25519_keypair_t kp;
   ed25519_public_key_t pk;
+  ed25519_signature_t sig1, sig2;
   char *mem_op_hex_tmp = NULL;
   (void) arg;
 
@@ -1342,6 +1355,11 @@ test_crypto_ed25519_encode(void *arg)
   tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk, buf));
   tt_mem_op(kp.pubkey.pubkey, OP_EQ, pk.pubkey, ED25519_PUBKEY_LEN);
 
+  tt_int_op(0, OP_EQ, ed25519_sign(&sig1, (const uint8_t*)"ABC", 3, &kp));
+  tt_int_op(0, OP_EQ, ed25519_signature_to_base64(buf, &sig1));
+  tt_int_op(0, OP_EQ, ed25519_signature_from_base64(&sig2, buf));
+  tt_mem_op(sig1.sig, OP_EQ, sig2.sig, ED25519_SIG_LEN);
+
   /* Test known value. */
   tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk,
                              "lVIuIctLjbGZGU5wKMNXxXlSE3cW4kaqkqm04u6pxvM"));

+ 29 - 5
src/test/test_dir.c

@@ -14,6 +14,7 @@
 #define NETWORKSTATUS_PRIVATE
 #include "or.h"
 #include "config.h"
+#include "crypto_ed25519.h"
 #include "directory.h"
 #include "dirserv.h"
 #include "dirvote.h"
@@ -23,6 +24,7 @@
 #include "routerlist.h"
 #include "routerparse.h"
 #include "test.h"
+#include "torcert.h"
 
 static void
 test_dir_nicknames(void *arg)
@@ -89,6 +91,7 @@ test_dir_formats(void *arg)
   routerlist_t *dir1 = NULL, *dir2 = NULL;
   or_options_t *options = get_options_mutable();
   const addr_policy_t *p;
+  time_t now = time(NULL);
 
   (void)arg;
   pk1 = pk_generate(0);
@@ -127,6 +130,22 @@ test_dir_formats(void *arg)
   ex2->prt_min = ex2->prt_max = 24;
   r2 = tor_malloc_zero(sizeof(routerinfo_t));
   r2->addr = 0x0a030201u; /* 10.3.2.1 */
+  ed25519_keypair_t kp1, kp2;
+  ed25519_secret_key_from_seed(&kp1.seckey,
+                          (const uint8_t*)"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY");
+  ed25519_public_key_generate(&kp1.pubkey, &kp1.seckey);
+  ed25519_secret_key_from_seed(&kp2.seckey,
+                          (const uint8_t*)"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+  ed25519_public_key_generate(&kp2.pubkey, &kp2.seckey);
+  r2->signing_key_cert = tor_cert_create(&kp1,
+                                         CERT_TYPE_ID_SIGNING,
+                                         &kp2.pubkey,
+                                         now, 86400,
+                                         CERT_FLAG_INCLUDE_SIGNING_KEY);
+  char cert_buf[256];
+  base64_encode(cert_buf, sizeof(cert_buf),
+                (const char*)r2->signing_key_cert->encoded,
+                r2->signing_key_cert->encoded_len);
   r2->platform = tor_strdup(platform);
   r2->cache_info.published_on = 5;
   r2->or_port = 9005;
@@ -150,7 +169,7 @@ test_dir_formats(void *arg)
   /* XXXX025 router_dump_to_string should really take this from ri.*/
   options->ContactInfo = tor_strdup("Magri White "
                                     "<magri@elsewhere.example.com>");
-  buf = router_dump_router_to_string(r1, pk2);
+  buf = router_dump_router_to_string(r1, pk2, NULL);
   tor_free(options->ContactInfo);
   tt_assert(buf);
 
@@ -183,7 +202,7 @@ test_dir_formats(void *arg)
   tt_str_op(buf,OP_EQ, buf2);
   tor_free(buf);
 
-  buf = router_dump_router_to_string(r1, pk2);
+  buf = router_dump_router_to_string(r1, pk2, NULL);
   tt_assert(buf);
   cp = buf;
   rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
@@ -201,6 +220,10 @@ test_dir_formats(void *arg)
 
   strlcpy(buf2,
           "router Fred 10.3.2.1 9005 0 0\n"
+          "identity-ed25519\n"
+          "-----BEGIN ED25519 CERT-----\n", sizeof(buf2));
+  strlcat(buf2, cert_buf, sizeof(buf2));
+  strlcat(buf2, "-----END ED25519 CERT-----\n"
           "platform Tor "VERSION" on ", sizeof(buf2));
   strlcat(buf2, get_uname(), sizeof(buf2));
   strlcat(buf2, "\n"
@@ -219,15 +242,16 @@ test_dir_formats(void *arg)
   strlcat(buf2, "ntor-onion-key "
           "skyinAnvardNostarsNomoonNowindormistsorsnow=\n", sizeof(buf2));
   strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
-  strlcat(buf2, "router-signature\n", sizeof(buf2));
+  strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2));
 
-  buf = router_dump_router_to_string(r2, pk1);
+  buf = router_dump_router_to_string(r2, pk1, &kp2);
+  tt_assert(buf);
   buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
                              * twice */
   tt_str_op(buf,OP_EQ, buf2);
   tor_free(buf);
 
-  buf = router_dump_router_to_string(r2, pk1);
+  buf = router_dump_router_to_string(r2, pk1, NULL);
   cp = buf;
   rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
   tt_assert(rp2);

+ 2 - 0
src/test/test_routerkeys.c

@@ -90,6 +90,7 @@ test_routerkeys_ed_certs(void *args)
   tor_cert_t *parsed_cert[2] = {NULL, NULL};
   time_t now = 1412094534;
   uint8_t *junk = NULL;
+  char *base64 = NULL;
 
   tt_int_op(0,==,ed25519_keypair_generate(&kp1, 0));
   tt_int_op(0,==,ed25519_keypair_generate(&kp2, 0));
@@ -186,6 +187,7 @@ test_routerkeys_ed_certs(void *args)
   tor_cert_free(parsed_cert[0]);
   tor_cert_free(parsed_cert[1]);
   tor_free(junk);
+  tor_free(base64);
 }
 
 static void