Commit 3b2fc659 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

Refactor consensus signature storage for multiple digests and flavors.

This patch introduces a new type called document_signature_t to represent the
signature of a consensus document.  Now, each consensus document can have up
to one document signature per voter per digest algorithm.  Also, each
detached-signatures document can have up to one signature per <voter,
algorithm, flavor>.
parent e1ddee8b
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -1448,6 +1448,39 @@ crypto_digest256(char *digest, const char *m, size_t len,
  return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
}

/** Set the digests_t in <b>ds_out</b> to contain every digest on the
 * <b>len</b> bytes in <b>m</b> that we know how to compute.  Return 0 on
 * success, -1 on failure. */
int
crypto_digest_all(digests_t *ds_out, const char *m, size_t len)
{
  digest_algorithm_t i;
  tor_assert(ds_out);
  memset(ds_out, 0, sizeof(*ds_out));
  if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0)
    return -1;
  for (i = DIGEST_SHA256; i < N_DIGEST_ALGORITHMS; ++i) {
    if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
      return -1;
  }
  return 0;
}

/** Return the name of an algorithm, as used in directory documents. */
const char *
crypto_digest_algorithm_get_name(digest_algorithm_t alg)
{
  switch (alg) {
    case DIGEST_SHA1:
      return "sha1";
    case DIGEST_SHA256:
      return "sha256";
    default:
      tor_fragile_assert();
      return "??unknown_digest??";
  }
}

/** Intermediate information about the digest of a stream of data. */
struct crypto_digest_env_t {
  union {
+18 −3
Original line number Diff line number Diff line
@@ -58,9 +58,22 @@
#define HEX_DIGEST256_LEN 64

typedef enum {
  DIGEST_SHA1,
  DIGEST_SHA256,
  DIGEST_SHA1 = 0,
  DIGEST_SHA256 = 1,
} digest_algorithm_t;
#define  N_DIGEST_ALGORITHMS (DIGEST_SHA256+1)

/** A set of all the digests we know how to compute, taken on a single
 * string.  Any digests that are shorter than 256 bits are right-padded
 * with 0 bits.
 *
 * Note that this representation wastes 12 bytes for the SHA1 case, so
 * don't use it for anything where we need to allocate a whole bunch at
 * once.
 **/
typedef struct {
  char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN];
} digests_t;

typedef struct crypto_pk_env_t crypto_pk_env_t;
typedef struct crypto_cipher_env_t crypto_cipher_env_t;
@@ -158,10 +171,12 @@ int crypto_cipher_decrypt_with_iv(crypto_cipher_env_t *env,
                                  char *to, size_t tolen,
                                  const char *from, size_t fromlen);

/* SHA-1 */
/* SHA-1 and other digests. */
int crypto_digest(char *digest, const char *m, size_t len);
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);
const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
crypto_digest_env_t *crypto_new_digest_env(void);
crypto_digest_env_t *crypto_new_digest256_env(digest_algorithm_t algorithm);
void crypto_free_digest_env(crypto_digest_env_t *digest);
+5 −5
Original line number Diff line number Diff line
@@ -2330,7 +2330,7 @@ client_likes_consensus(networkstatus_t *v, const char *want_url)

  dir_split_resource_into_fingerprints(want_url, want_authorities, NULL, 0, 0);
  need_at_least = smartlist_len(want_authorities)/2+1;
  SMARTLIST_FOREACH(want_authorities, const char *, d, {
  SMARTLIST_FOREACH_BEGIN(want_authorities, const char *, d) {
    char want_digest[DIGEST_LEN];
    size_t want_len = strlen(d)/2;
    if (want_len > DIGEST_LEN)
@@ -2341,18 +2341,18 @@ client_likes_consensus(networkstatus_t *v, const char *want_url)
      continue;
    };

    SMARTLIST_FOREACH(v->voters, networkstatus_voter_info_t *, vi, {
      if (vi->signature &&
    SMARTLIST_FOREACH_BEGIN(v->voters, networkstatus_voter_info_t *, vi) {
      if (smartlist_len(vi->sigs) &&
          !memcmp(vi->identity_digest, want_digest, want_len)) {
        have++;
        break;
      };
    });
    } SMARTLIST_FOREACH_END(vi);

    /* early exit, if we already have enough */
    if (have >= need_at_least)
      break;
  });
  } SMARTLIST_FOREACH_END(d);

  SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d));
  smartlist_free(want_authorities);
+6 −1
Original line number Diff line number Diff line
@@ -2597,12 +2597,17 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
  voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
  voter->nickname = tor_strdup(options->Nickname);
  memcpy(voter->identity_digest, identity_digest, DIGEST_LEN);
  voter->sigs = smartlist_create();
  {
    document_signature_t *sig = tor_malloc_zero(sizeof(document_signature_t));
    memcpy(sig->identity_digest, identity_digest, DIGEST_LEN);
    memcpy(sig->signing_key_digest, signing_key_digest, DIGEST_LEN);
  }
  voter->address = hostname;
  voter->addr = addr;
  voter->dir_port = options->DirPort;
  voter->or_port = options->ORPort;
  voter->contact = tor_strdup(contact);
  memcpy(voter->signing_key_digest, signing_key_digest, DIGEST_LEN);
  if (options->V3AuthUseLegacyKey) {
    authority_cert_t *c = get_my_v3_legacy_cert();
    if (c) {
+138 −73
Original line number Diff line number Diff line
@@ -251,6 +251,19 @@ get_voter(const networkstatus_t *vote)
  return smartlist_get(vote->voters, 0);
}

/** DOCDOC */
document_signature_t *
voter_get_sig_by_algorithm(const networkstatus_voter_info_t *voter,
                           digest_algorithm_t alg)
{
  if (!voter->sigs)
    return NULL;
  SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
    if (sig->alg == alg)
      return sig);
  return NULL;
}

/** Temporary structure used in constructing a list of dir-source entries
 * for a consensus.  One of these is generated for every vote, and one more
 * for every legacy key in each vote. */
@@ -782,8 +795,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
  /* Add the authority sections. */
  {
    smartlist_t *dir_sources = smartlist_create();
    SMARTLIST_FOREACH(votes, networkstatus_t *, v,
    {
    SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
      dir_src_ent_t *e = tor_malloc_zero(sizeof(dir_src_ent_t));
      e->v = v;
      e->digest = get_voter(v)->identity_digest;
@@ -797,7 +809,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
        e_legacy->is_legacy = 1;
        smartlist_add(dir_sources, e_legacy);
      }
    });
    } SMARTLIST_FOREACH_END(v);
    smartlist_sort(dir_sources, _compare_dir_src_ents_by_authority_id);

    SMARTLIST_FOREACH(dir_sources, const dir_src_ent_t *, e,
@@ -1347,84 +1359,123 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
                                      const char **msg_out)
{
  int r = 0;
  const char *flavor;
  smartlist_t *siglist;
  tor_assert(sigs);
  tor_assert(target);
  tor_assert(target->type == NS_TYPE_CONSENSUS);

  flavor = networkstatus_get_flavor_name(target->flavor);

  /* Do the times seem right? */
  if (target->valid_after != sigs->valid_after) {
    puts("A");
    *msg_out = "Valid-After times do not match "
      "when adding detached signatures to consensus";
    return -1;
  }
  if (target->fresh_until != sigs->fresh_until) {
    puts("B");
    *msg_out = "Fresh-until times do not match "
      "when adding detached signatures to consensus";
    return -1;
  }
  if (target->valid_until != sigs->valid_until) {
    puts("C");
    *msg_out = "Valid-until times do not match "
      "when adding detached signatures to consensus";
    return -1;
  }
  /* Are they the same consensus? */
  if (memcmp(target->networkstatus_digest, sigs->networkstatus_digest,
             DIGEST_LEN)) {
    *msg_out = "Digest mismatch when adding detached signatures to consensus";
  siglist = strmap_get(sigs->signatures, flavor);
  if (!siglist) {
    puts("D");
    *msg_out = "No signatures for given consensus flavor";
    return -1;
  }

  /** Make sure all the digests we know match, and at least one matches. */
  {
    digests_t *digests = strmap_get(sigs->digests, flavor);
    int n_matches = 0;
    digest_algorithm_t alg;
    if (!digests) {
      puts("D");
      *msg_out = "No digests for given consensus flavor";
      return -1;
    }
    for (alg = DIGEST_SHA1; alg < N_DIGEST_ALGORITHMS; ++alg) {
      if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
        if (!memcmp(target->digests.d[alg], digests->d[alg], DIGEST256_LEN)) {
          ++n_matches;
        } else {
          printf("F %d\n", alg);
          printf("%s\n", hex_str(target->digests.d[alg], DIGEST256_LEN));
          printf("%s\n", hex_str(digests->d[alg], DIGEST256_LEN));
          *msg_out = "Mismatched digest.";
          return -1;
        }
      }
    }
    if (!n_matches) {
      puts("G");
      *msg_out = "No regognized digests for given consensus flavor";
    }
  }

  /* For each voter in src... */
  SMARTLIST_FOREACH_BEGIN(sigs->signatures, networkstatus_voter_info_t *,
                          src_voter) {
  SMARTLIST_FOREACH_BEGIN(siglist, document_signature_t *, sig) {
    char voter_identity[HEX_DIGEST_LEN+1];
    networkstatus_voter_info_t *target_voter =
        networkstatus_get_voter_by_id(target, src_voter->identity_digest);
      networkstatus_get_voter_by_id(target, sig->identity_digest);
    authority_cert_t *cert = NULL;
    const char *algorithm;
    document_signature_t *old_sig = NULL;

    algorithm = crypto_digest_algorithm_get_name(sig->alg);

    base16_encode(voter_identity, sizeof(voter_identity),
                    src_voter->identity_digest, DIGEST_LEN);
      log_info(LD_DIR, "Looking at signature from %s", voter_identity);
                  sig->identity_digest, DIGEST_LEN);
    log_info(LD_DIR, "Looking at signature from %s using %s", voter_identity,
             algorithm);
    /* If the target doesn't know about this voter, then forget it. */
    if (!target_voter) {
        log_info(LD_DIR, "We do not know about %s", voter_identity);
      log_info(LD_DIR, "We do not know any voter with ID %s", voter_identity);
      continue;
    }

    old_sig = voter_get_sig_by_algorithm(target_voter, sig->alg);

    /* If the target already has a good signature from this voter, then skip
     * this one. */
      if (target_voter->good_signature) {
        log_info(LD_DIR, "We already have a good signature from %s",
                         voter_identity);
    if (old_sig && old_sig->good_signature) {
      log_info(LD_DIR, "We already have a good signature from %s using %s",
               voter_identity, algorithm);
      continue;
    }

    /* Try checking the signature if we haven't already. */
      if (!src_voter->good_signature && !src_voter->bad_signature) {
        cert = authority_cert_get_by_digests(src_voter->identity_digest,
                                             src_voter->signing_key_digest);
        if (cert) {
          networkstatus_check_voter_signature(target, src_voter, cert);
        }
    if (!sig->good_signature && !sig->bad_signature) {
      cert = authority_cert_get_by_digests(sig->identity_digest,
                                           sig->signing_key_digest);
      if (cert)
        networkstatus_check_document_signature(target, sig, cert);
    }

    /* If this signature is good, or we don't have any signature yet,
       * then add it. */
      if (src_voter->good_signature || !target_voter->signature) {
        log_info(LD_DIR, "Adding signature from %s", voter_identity);
     * then maybe add it. */
    if (sig->good_signature || !old_sig || old_sig->bad_signature) {
      log_info(LD_DIR, "Adding signature from %s with %s", voter_identity,
               algorithm);
      ++r;
        tor_free(target_voter->signature);
        target_voter->signature =
          tor_memdup(src_voter->signature, src_voter->signature_len);
        memcpy(target_voter->signing_key_digest, src_voter->signing_key_digest,
               DIGEST_LEN);
        target_voter->signature_len = src_voter->signature_len;
        target_voter->good_signature = src_voter->good_signature;
        target_voter->bad_signature = src_voter->bad_signature;
      if (old_sig) {
        smartlist_remove(target_voter->sigs, old_sig);
        document_signature_free(old_sig);
      }
      smartlist_add(target_voter->sigs, document_signature_dup(sig));
    } else {
      log_info(LD_DIR, "Not adding signature from %s", voter_identity);
    }
  } SMARTLIST_FOREACH_END(src_voter);
  } SMARTLIST_FOREACH_END(sig);

  return r;
}
@@ -1441,6 +1492,8 @@ networkstatus_get_detached_signatures(networkstatus_t *consensus)
  tor_assert(consensus);
  tor_assert(consensus->type == NS_TYPE_CONSENSUS);

  tor_assert(consensus->flavor == FLAV_NS);

  elements = smartlist_create();

  {
@@ -1448,7 +1501,7 @@ networkstatus_get_detached_signatures(networkstatus_t *consensus)
      vu_buf[ISO_TIME_LEN+1];
    char d[HEX_DIGEST_LEN+1];

    base16_encode(d, sizeof(d), consensus->networkstatus_digest, DIGEST_LEN);
    base16_encode(d, sizeof(d), consensus->digests.d[DIGEST_SHA1], DIGEST_LEN);
    format_iso_time(va_buf, consensus->valid_after);
    format_iso_time(fu_buf, consensus->fresh_until);
    format_iso_time(vu_buf, consensus->valid_until);
@@ -1461,26 +1514,26 @@ networkstatus_get_detached_signatures(networkstatus_t *consensus)
    smartlist_add(elements, tor_strdup(buf));
  }

  SMARTLIST_FOREACH(consensus->voters, networkstatus_voter_info_t *, v,
    {
  SMARTLIST_FOREACH_BEGIN(consensus->voters, networkstatus_voter_info_t *, v) {
    SMARTLIST_FOREACH_BEGIN(v->sigs, document_signature_t *, sig) {
      char sk[HEX_DIGEST_LEN+1];
      char id[HEX_DIGEST_LEN+1];
      if (!v->signature || v->bad_signature)
      if (!sig->signature || sig->bad_signature || sig->alg != DIGEST_SHA1)
        continue;
      ++n_sigs;
      base16_encode(sk, sizeof(sk), v->signing_key_digest, DIGEST_LEN);
      base16_encode(id, sizeof(id), v->identity_digest, DIGEST_LEN);
      base16_encode(sk, sizeof(sk), sig->signing_key_digest, DIGEST_LEN);
      base16_encode(id, sizeof(id), sig->identity_digest, DIGEST_LEN);
      tor_snprintf(buf, sizeof(buf),
                   "directory-signature %s %s\n-----BEGIN SIGNATURE-----\n",
                   id, sk);
      smartlist_add(elements, tor_strdup(buf));
      base64_encode(buf, sizeof(buf), v->signature, v->signature_len);
      base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len);
      strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf));
      smartlist_add(elements, tor_strdup(buf));
    });
    } SMARTLIST_FOREACH_END(sig);
  } SMARTLIST_FOREACH_END(v);

  result = smartlist_join_strings(elements, "", 0, NULL);

  SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
  smartlist_free(elements);
  if (!n_sigs)
@@ -1493,13 +1546,15 @@ void
ns_detached_signatures_free(ns_detached_signatures_t *s)
{
  if (s->signatures) {
    SMARTLIST_FOREACH(s->signatures, networkstatus_voter_info_t *, v,
      {
        tor_free(v->signature);
        tor_free(v);
      });
    smartlist_free(s->signatures);
    STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) {
      SMARTLIST_FOREACH(sigs, document_signature_t *, sig,
                        document_signature_free(sig));
      smartlist_free(sigs);
    } STRMAP_FOREACH_END;
    strmap_free(s->signatures, NULL);
    strmap_free(s->digests, _tor_free);
  }

  tor_free(s);
}

@@ -1936,7 +1991,13 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
  }
  tor_assert(smartlist_len(vote->voters) == 1);
  vi = get_voter(vote);
  tor_assert(vi->good_signature == 1);
  {
    int any_sig_good = 0;
    SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig,
                      if (sig->good_signature)
                        any_sig_good = 1);
    tor_assert(any_sig_good);
  }
  ds = trusteddirserver_get_by_v3_auth_digest(vi->identity_digest);
  if (!ds) {
    char *keys = list_v3_auth_ids();
@@ -2218,8 +2279,12 @@ dirvote_add_signatures_to_pending_consensus(
    goto err;
  }

  {
    smartlist_t *sig_list = strmap_get(sigs->signatures,
                    networkstatus_get_flavor_name(pending_consensus->flavor));
    log_info(LD_DIR, "Have %d signatures for adding to consensus.",
                   smartlist_len(sigs->signatures));
             sig_list ? smartlist_len(sig_list) : 0);
  }
  r = networkstatus_add_detached_signatures(pending_consensus,
                                            sigs, msg_out);
  log_info(LD_DIR,"Added %d signatures to consensus.", r);
@@ -2406,12 +2471,12 @@ dirvote_get_vote(const char *fp, int flags)
  } else {
    if (pending_vote_list && include_pending) {
      SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, pv,
        if (!memcmp(pv->vote->networkstatus_digest, fp, DIGEST_LEN))
        if (!memcmp(pv->vote->digests.d[DIGEST_SHA1], fp, DIGEST_LEN))
          return pv->vote_body);
    }
    if (previous_vote_list && include_previous) {
      SMARTLIST_FOREACH(previous_vote_list, pending_vote_t *, pv,
        if (!memcmp(pv->vote->networkstatus_digest, fp, DIGEST_LEN))
        if (!memcmp(pv->vote->digests.d[DIGEST_SHA1], fp, DIGEST_LEN))
          return pv->vote_body);
    }
  }
Loading