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>.
This commit is contained in:
Nick Mathewson 2009-09-16 17:01:01 -04:00
parent e1ddee8bbe
commit 3b2fc659a8
10 changed files with 589 additions and 306 deletions

View File

@ -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); 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. */ /** Intermediate information about the digest of a stream of data. */
struct crypto_digest_env_t { struct crypto_digest_env_t {
union { union {

View File

@ -58,9 +58,22 @@
#define HEX_DIGEST256_LEN 64 #define HEX_DIGEST256_LEN 64
typedef enum { typedef enum {
DIGEST_SHA1, DIGEST_SHA1 = 0,
DIGEST_SHA256, DIGEST_SHA256 = 1,
} digest_algorithm_t; } 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_pk_env_t crypto_pk_env_t;
typedef struct crypto_cipher_env_t crypto_cipher_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, char *to, size_t tolen,
const char *from, size_t fromlen); 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_digest(char *digest, const char *m, size_t len);
int crypto_digest256(char *digest, const char *m, size_t len, int crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm); 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_digest_env(void);
crypto_digest_env_t *crypto_new_digest256_env(digest_algorithm_t algorithm); crypto_digest_env_t *crypto_new_digest256_env(digest_algorithm_t algorithm);
void crypto_free_digest_env(crypto_digest_env_t *digest); void crypto_free_digest_env(crypto_digest_env_t *digest);

View File

@ -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); dir_split_resource_into_fingerprints(want_url, want_authorities, NULL, 0, 0);
need_at_least = smartlist_len(want_authorities)/2+1; 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]; char want_digest[DIGEST_LEN];
size_t want_len = strlen(d)/2; size_t want_len = strlen(d)/2;
if (want_len > DIGEST_LEN) if (want_len > DIGEST_LEN)
@ -2341,18 +2341,18 @@ client_likes_consensus(networkstatus_t *v, const char *want_url)
continue; continue;
}; };
SMARTLIST_FOREACH(v->voters, networkstatus_voter_info_t *, vi, { SMARTLIST_FOREACH_BEGIN(v->voters, networkstatus_voter_info_t *, vi) {
if (vi->signature && if (smartlist_len(vi->sigs) &&
!memcmp(vi->identity_digest, want_digest, want_len)) { !memcmp(vi->identity_digest, want_digest, want_len)) {
have++; have++;
break; break;
}; };
}); } SMARTLIST_FOREACH_END(vi);
/* early exit, if we already have enough */ /* early exit, if we already have enough */
if (have >= need_at_least) if (have >= need_at_least)
break; break;
}); } SMARTLIST_FOREACH_END(d);
SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d)); SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d));
smartlist_free(want_authorities); smartlist_free(want_authorities);

View File

@ -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 = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
voter->nickname = tor_strdup(options->Nickname); voter->nickname = tor_strdup(options->Nickname);
memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); 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->address = hostname;
voter->addr = addr; voter->addr = addr;
voter->dir_port = options->DirPort; voter->dir_port = options->DirPort;
voter->or_port = options->ORPort; voter->or_port = options->ORPort;
voter->contact = tor_strdup(contact); voter->contact = tor_strdup(contact);
memcpy(voter->signing_key_digest, signing_key_digest, DIGEST_LEN);
if (options->V3AuthUseLegacyKey) { if (options->V3AuthUseLegacyKey) {
authority_cert_t *c = get_my_v3_legacy_cert(); authority_cert_t *c = get_my_v3_legacy_cert();
if (c) { if (c) {

View File

@ -251,6 +251,19 @@ get_voter(const networkstatus_t *vote)
return smartlist_get(vote->voters, 0); 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 /** 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 a consensus. One of these is generated for every vote, and one more
* for every legacy key in each vote. */ * for every legacy key in each vote. */
@ -782,8 +795,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Add the authority sections. */ /* Add the authority sections. */
{ {
smartlist_t *dir_sources = smartlist_create(); 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)); dir_src_ent_t *e = tor_malloc_zero(sizeof(dir_src_ent_t));
e->v = v; e->v = v;
e->digest = get_voter(v)->identity_digest; e->digest = get_voter(v)->identity_digest;
@ -797,7 +809,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
e_legacy->is_legacy = 1; e_legacy->is_legacy = 1;
smartlist_add(dir_sources, e_legacy); smartlist_add(dir_sources, e_legacy);
} }
}); } SMARTLIST_FOREACH_END(v);
smartlist_sort(dir_sources, _compare_dir_src_ents_by_authority_id); smartlist_sort(dir_sources, _compare_dir_src_ents_by_authority_id);
SMARTLIST_FOREACH(dir_sources, const dir_src_ent_t *, e, 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) const char **msg_out)
{ {
int r = 0; int r = 0;
const char *flavor;
smartlist_t *siglist;
tor_assert(sigs); tor_assert(sigs);
tor_assert(target); tor_assert(target);
tor_assert(target->type == NS_TYPE_CONSENSUS); tor_assert(target->type == NS_TYPE_CONSENSUS);
flavor = networkstatus_get_flavor_name(target->flavor);
/* Do the times seem right? */ /* Do the times seem right? */
if (target->valid_after != sigs->valid_after) { if (target->valid_after != sigs->valid_after) {
puts("A");
*msg_out = "Valid-After times do not match " *msg_out = "Valid-After times do not match "
"when adding detached signatures to consensus"; "when adding detached signatures to consensus";
return -1; return -1;
} }
if (target->fresh_until != sigs->fresh_until) { if (target->fresh_until != sigs->fresh_until) {
puts("B");
*msg_out = "Fresh-until times do not match " *msg_out = "Fresh-until times do not match "
"when adding detached signatures to consensus"; "when adding detached signatures to consensus";
return -1; return -1;
} }
if (target->valid_until != sigs->valid_until) { if (target->valid_until != sigs->valid_until) {
puts("C");
*msg_out = "Valid-until times do not match " *msg_out = "Valid-until times do not match "
"when adding detached signatures to consensus"; "when adding detached signatures to consensus";
return -1; return -1;
} }
/* Are they the same consensus? */ siglist = strmap_get(sigs->signatures, flavor);
if (memcmp(target->networkstatus_digest, sigs->networkstatus_digest, if (!siglist) {
DIGEST_LEN)) { puts("D");
*msg_out = "Digest mismatch when adding detached signatures to consensus"; *msg_out = "No signatures for given consensus flavor";
return -1; return -1;
} }
/* For each voter in src... */ /** Make sure all the digests we know match, and at least one matches. */
SMARTLIST_FOREACH_BEGIN(sigs->signatures, networkstatus_voter_info_t *, {
src_voter) { digests_t *digests = strmap_get(sigs->digests, flavor);
char voter_identity[HEX_DIGEST_LEN+1]; int n_matches = 0;
networkstatus_voter_info_t *target_voter = digest_algorithm_t alg;
networkstatus_get_voter_by_id(target, src_voter->identity_digest); if (!digests) {
authority_cert_t *cert = NULL; puts("D");
*msg_out = "No digests for given consensus flavor";
base16_encode(voter_identity, sizeof(voter_identity), return -1;
src_voter->identity_digest, DIGEST_LEN); }
log_info(LD_DIR, "Looking at signature from %s", voter_identity); for (alg = DIGEST_SHA1; alg < N_DIGEST_ALGORITHMS; ++alg) {
/* If the target doesn't know about this voter, then forget it. */ if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
if (!target_voter) { if (!memcmp(target->digests.d[alg], digests->d[alg], DIGEST256_LEN)) {
log_info(LD_DIR, "We do not know about %s", voter_identity); ++n_matches;
continue; } else {
} printf("F %d\n", alg);
printf("%s\n", hex_str(target->digests.d[alg], DIGEST256_LEN));
/* If the target already has a good signature from this voter, then skip printf("%s\n", hex_str(digests->d[alg], DIGEST256_LEN));
* this one. */ *msg_out = "Mismatched digest.";
if (target_voter->good_signature) { return -1;
log_info(LD_DIR, "We already have a good signature from %s",
voter_identity);
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 (!n_matches) {
puts("G");
*msg_out = "No regognized digests for given consensus flavor";
}
}
/* If this signature is good, or we don't have any signature yet, /* For each voter in src... */
* then add it. */ SMARTLIST_FOREACH_BEGIN(siglist, document_signature_t *, sig) {
if (src_voter->good_signature || !target_voter->signature) { char voter_identity[HEX_DIGEST_LEN+1];
log_info(LD_DIR, "Adding signature from %s", voter_identity); networkstatus_voter_info_t *target_voter =
++r; networkstatus_get_voter_by_id(target, sig->identity_digest);
tor_free(target_voter->signature); authority_cert_t *cert = NULL;
target_voter->signature = const char *algorithm;
tor_memdup(src_voter->signature, src_voter->signature_len); document_signature_t *old_sig = NULL;
memcpy(target_voter->signing_key_digest, src_voter->signing_key_digest,
DIGEST_LEN); algorithm = crypto_digest_algorithm_get_name(sig->alg);
target_voter->signature_len = src_voter->signature_len;
target_voter->good_signature = src_voter->good_signature; base16_encode(voter_identity, sizeof(voter_identity),
target_voter->bad_signature = src_voter->bad_signature; sig->identity_digest, DIGEST_LEN);
} else { log_info(LD_DIR, "Looking at signature from %s using %s", voter_identity,
log_info(LD_DIR, "Not adding signature from %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 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 (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 (!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 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;
if (old_sig) {
smartlist_remove(target_voter->sigs, old_sig);
document_signature_free(old_sig);
} }
} SMARTLIST_FOREACH_END(src_voter); smartlist_add(target_voter->sigs, document_signature_dup(sig));
} else {
log_info(LD_DIR, "Not adding signature from %s", voter_identity);
}
} SMARTLIST_FOREACH_END(sig);
return r; return r;
} }
@ -1441,6 +1492,8 @@ networkstatus_get_detached_signatures(networkstatus_t *consensus)
tor_assert(consensus); tor_assert(consensus);
tor_assert(consensus->type == NS_TYPE_CONSENSUS); tor_assert(consensus->type == NS_TYPE_CONSENSUS);
tor_assert(consensus->flavor == FLAV_NS);
elements = smartlist_create(); elements = smartlist_create();
{ {
@ -1448,7 +1501,7 @@ networkstatus_get_detached_signatures(networkstatus_t *consensus)
vu_buf[ISO_TIME_LEN+1]; vu_buf[ISO_TIME_LEN+1];
char d[HEX_DIGEST_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(va_buf, consensus->valid_after);
format_iso_time(fu_buf, consensus->fresh_until); format_iso_time(fu_buf, consensus->fresh_until);
format_iso_time(vu_buf, consensus->valid_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_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 sk[HEX_DIGEST_LEN+1];
char id[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; continue;
++n_sigs; ++n_sigs;
base16_encode(sk, sizeof(sk), v->signing_key_digest, DIGEST_LEN); base16_encode(sk, sizeof(sk), sig->signing_key_digest, DIGEST_LEN);
base16_encode(id, sizeof(id), v->identity_digest, DIGEST_LEN); base16_encode(id, sizeof(id), sig->identity_digest, DIGEST_LEN);
tor_snprintf(buf, sizeof(buf), tor_snprintf(buf, sizeof(buf),
"directory-signature %s %s\n-----BEGIN SIGNATURE-----\n", "directory-signature %s %s\n-----BEGIN SIGNATURE-----\n",
id, sk); id, sk);
smartlist_add(elements, tor_strdup(buf)); 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)); strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf));
smartlist_add(elements, tor_strdup(buf)); smartlist_add(elements, tor_strdup(buf));
}); } SMARTLIST_FOREACH_END(sig);
} SMARTLIST_FOREACH_END(v);
result = smartlist_join_strings(elements, "", 0, NULL); result = smartlist_join_strings(elements, "", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements); smartlist_free(elements);
if (!n_sigs) if (!n_sigs)
@ -1493,13 +1546,15 @@ void
ns_detached_signatures_free(ns_detached_signatures_t *s) ns_detached_signatures_free(ns_detached_signatures_t *s)
{ {
if (s->signatures) { if (s->signatures) {
SMARTLIST_FOREACH(s->signatures, networkstatus_voter_info_t *, v, STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) {
{ SMARTLIST_FOREACH(sigs, document_signature_t *, sig,
tor_free(v->signature); document_signature_free(sig));
tor_free(v); smartlist_free(sigs);
}); } STRMAP_FOREACH_END;
smartlist_free(s->signatures); strmap_free(s->signatures, NULL);
strmap_free(s->digests, _tor_free);
} }
tor_free(s); 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); tor_assert(smartlist_len(vote->voters) == 1);
vi = get_voter(vote); 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); ds = trusteddirserver_get_by_v3_auth_digest(vi->identity_digest);
if (!ds) { if (!ds) {
char *keys = list_v3_auth_ids(); char *keys = list_v3_auth_ids();
@ -2218,8 +2279,12 @@ dirvote_add_signatures_to_pending_consensus(
goto err; goto err;
} }
log_info(LD_DIR, "Have %d signatures for adding to consensus.", {
smartlist_len(sigs->signatures)); 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.",
sig_list ? smartlist_len(sig_list) : 0);
}
r = networkstatus_add_detached_signatures(pending_consensus, r = networkstatus_add_detached_signatures(pending_consensus,
sigs, msg_out); sigs, msg_out);
log_info(LD_DIR,"Added %d signatures to consensus.", r); log_info(LD_DIR,"Added %d signatures to consensus.", r);
@ -2406,12 +2471,12 @@ dirvote_get_vote(const char *fp, int flags)
} else { } else {
if (pending_vote_list && include_pending) { if (pending_vote_list && include_pending) {
SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, pv, 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); return pv->vote_body);
} }
if (previous_vote_list && include_previous) { if (previous_vote_list && include_previous) {
SMARTLIST_FOREACH(previous_vote_list, pending_vote_t *, pv, 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); return pv->vote_body);
} }
} }

View File

@ -279,7 +279,25 @@ networkstatus_v2_free(networkstatus_v2_t *ns)
tor_free(ns); tor_free(ns);
} }
/** Clear all storage held in <b>ns</b>. */ /** Free all storage held in <b>sig</b> */
void
document_signature_free(document_signature_t *sig)
{
tor_free(sig->signature);
tor_free(sig);
}
/** Return a newly allocated copy of <b>sig</b> */
document_signature_t *
document_signature_dup(const document_signature_t *sig)
{
document_signature_t *r = tor_memdup(sig, sizeof(document_signature_t));
if (r->signature)
r->signature = tor_memdup(sig->signature, sig->signature_len);
return r;
}
/** Free all storage held in <b>ns</b>. */
void void
networkstatus_vote_free(networkstatus_t *ns) networkstatus_vote_free(networkstatus_t *ns)
{ {
@ -301,14 +319,17 @@ networkstatus_vote_free(networkstatus_t *ns)
smartlist_free(ns->supported_methods); smartlist_free(ns->supported_methods);
} }
if (ns->voters) { if (ns->voters) {
SMARTLIST_FOREACH(ns->voters, networkstatus_voter_info_t *, voter, SMARTLIST_FOREACH_BEGIN(ns->voters, networkstatus_voter_info_t *, voter) {
{
tor_free(voter->nickname); tor_free(voter->nickname);
tor_free(voter->address); tor_free(voter->address);
tor_free(voter->contact); tor_free(voter->contact);
tor_free(voter->signature); if (voter->sigs) {
SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
document_signature_free(sig));
smartlist_free(voter->sigs);
}
tor_free(voter); tor_free(voter);
}); } SMARTLIST_FOREACH_END(voter);
smartlist_free(ns->voters); smartlist_free(ns->voters);
} }
if (ns->cert) if (ns->cert)
@ -347,34 +368,38 @@ networkstatus_get_voter_by_id(networkstatus_t *vote,
return NULL; return NULL;
} }
/** Check whether the signature on <b>voter</b> is correctly signed by /** Check whether the signature <b>sig</b> is correctly signed with the
* the signing key of <b>cert</b>. Return -1 if <b>cert</b> doesn't match the * signing key in <b>cert</b>. Return -1 if <b>cert</b> doesn't match the
* signing key; otherwise set the good_signature or bad_signature flag on * signing key; otherwise set the good_signature or bad_signature flag on
* <b>voter</b>, and return 0. */ * <b>voter</b>, and return 0. */
/* (private; exposed for testing.) */
int int
networkstatus_check_voter_signature(networkstatus_t *consensus, networkstatus_check_document_signature(const networkstatus_t *consensus,
networkstatus_voter_info_t *voter, document_signature_t *sig,
authority_cert_t *cert) const authority_cert_t *cert)
{ {
char d[DIGEST_LEN]; char key_digest[DIGEST_LEN];
const int dlen = sig->alg == DIGEST_SHA1 ? DIGEST_LEN : DIGEST256_LEN;
char *signed_digest; char *signed_digest;
size_t signed_digest_len; size_t signed_digest_len;
if (crypto_pk_get_digest(cert->signing_key, d)<0)
if (crypto_pk_get_digest(cert->signing_key, key_digest)<0)
return -1; return -1;
if (memcmp(voter->signing_key_digest, d, DIGEST_LEN)) if (memcmp(sig->signing_key_digest, key_digest, DIGEST_LEN) ||
memcmp(sig->identity_digest, cert->cache_info.identity_digest,
DIGEST_LEN))
return -1; return -1;
signed_digest_len = crypto_pk_keysize(cert->signing_key); signed_digest_len = crypto_pk_keysize(cert->signing_key);
signed_digest = tor_malloc(signed_digest_len); signed_digest = tor_malloc(signed_digest_len);
if (crypto_pk_public_checksig(cert->signing_key, if (crypto_pk_public_checksig(cert->signing_key,
signed_digest, signed_digest,
voter->signature, sig->signature,
voter->signature_len) != DIGEST_LEN || sig->signature_len) < dlen ||
memcmp(signed_digest, consensus->networkstatus_digest, DIGEST_LEN)) { memcmp(signed_digest, consensus->digests.d[sig->alg], dlen)) {
log_warn(LD_DIR, "Got a bad signature on a networkstatus vote"); log_warn(LD_DIR, "Got a bad signature on a networkstatus vote");
voter->bad_signature = 1; sig->bad_signature = 1;
} else { } else {
voter->good_signature = 1; sig->good_signature = 1;
} }
tor_free(signed_digest); tor_free(signed_digest);
return 0; return 0;
@ -407,37 +432,52 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
tor_assert(consensus->type == NS_TYPE_CONSENSUS); tor_assert(consensus->type == NS_TYPE_CONSENSUS);
SMARTLIST_FOREACH(consensus->voters, networkstatus_voter_info_t *, voter, SMARTLIST_FOREACH_BEGIN(consensus->voters, networkstatus_voter_info_t *,
{ voter) {
if (!voter->good_signature && !voter->bad_signature && voter->signature) { int good_here = 0;
/* we can try to check the signature. */ int bad_here = 0;
int is_v3_auth = trusteddirserver_get_by_v3_auth_digest( int missing_key_here = 0;
voter->identity_digest) != NULL; SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) {
authority_cert_t *cert = if (!sig->good_signature && !sig->bad_signature &&
authority_cert_get_by_digests(voter->identity_digest, sig->signature) {
voter->signing_key_digest); /* we can try to check the signature. */
if (!is_v3_auth) { int is_v3_auth = trusteddirserver_get_by_v3_auth_digest(
smartlist_add(unrecognized, voter); sig->identity_digest) != NULL;
++n_unknown; authority_cert_t *cert =
continue; authority_cert_get_by_digests(sig->identity_digest,
} else if (!cert || cert->expires < now) { sig->signing_key_digest);
smartlist_add(need_certs_from, voter); tor_assert(!memcmp(sig->identity_digest, voter->identity_digest,
++n_missing_key; DIGEST_LEN));
continue;
if (!is_v3_auth) {
smartlist_add(unrecognized, voter);
++n_unknown;
continue;
} else if (!cert || cert->expires < now) {
smartlist_add(need_certs_from, voter);
++missing_key_here;
continue;
}
if (networkstatus_check_document_signature(consensus, sig, cert) < 0) {
smartlist_add(need_certs_from, voter);
++missing_key_here;
continue;
}
} }
if (networkstatus_check_voter_signature(consensus, voter, cert) < 0) { if (sig->good_signature)
smartlist_add(need_certs_from, voter); ++good_here;
++n_missing_key; else if (sig->bad_signature)
continue; ++bad_here;
} } SMARTLIST_FOREACH_END(sig);
} if (good_here)
if (voter->good_signature)
++n_good; ++n_good;
else if (voter->bad_signature) else if (bad_here)
++n_bad; ++n_bad;
else if (missing_key_here)
++n_missing_key;
else else
++n_no_signature; ++n_no_signature;
}); } SMARTLIST_FOREACH_END(voter);
/* Now see whether we're missing any voters entirely. */ /* Now see whether we're missing any voters entirely. */
SMARTLIST_FOREACH(router_get_trusted_dir_servers(), SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
@ -1434,8 +1474,7 @@ networkstatus_set_current_consensus(const char *consensus, unsigned flags)
} }
if (current_consensus && if (current_consensus &&
!memcmp(c->networkstatus_digest, current_consensus->networkstatus_digest, !memcmp(&c->digests, &current_consensus->digests, sizeof(c->digests))) {
DIGEST_LEN)) {
/* We already have this one. That's a failure. */ /* We already have this one. That's a failure. */
log_info(LD_DIR, "Got a consensus we already have"); log_info(LD_DIR, "Got a consensus we already have");
goto done; goto done;
@ -1669,10 +1708,8 @@ download_status_map_update_from_v2_networkstatus(void)
v2_download_status_map = digestmap_new(); v2_download_status_map = digestmap_new();
dl_status = digestmap_new(); dl_status = digestmap_new();
SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns, SMARTLIST_FOREACH_BEGIN(networkstatus_v2_list, networkstatus_v2_t *, ns) {
{ SMARTLIST_FOREACH_BEGIN(ns->entries, routerstatus_t *, rs) {
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
{
const char *d = rs->descriptor_digest; const char *d = rs->descriptor_digest;
download_status_t *s; download_status_t *s;
if (digestmap_get(dl_status, d)) if (digestmap_get(dl_status, d))
@ -1681,8 +1718,8 @@ download_status_map_update_from_v2_networkstatus(void)
s = tor_malloc_zero(sizeof(download_status_t)); s = tor_malloc_zero(sizeof(download_status_t));
} }
digestmap_set(dl_status, d, s); digestmap_set(dl_status, d, s);
}); } SMARTLIST_FOREACH_END(rs);
}); } SMARTLIST_FOREACH_END(ns);
digestmap_free(v2_download_status_map, _tor_free); digestmap_free(v2_download_status_map, _tor_free);
v2_download_status_map = dl_status; v2_download_status_map = dl_status;
networkstatus_v2_list_has_changed = 0; networkstatus_v2_list_has_changed = 0;
@ -1930,6 +1967,22 @@ networkstatus_get_param(networkstatus_t *ns, const char *param_name,
return default_val; return default_val;
} }
/** Return the name of the consensus flavor <b>flav</b> as used to identify
* the flavor in directory documents. */
const char *
networkstatus_get_flavor_name(consensus_flavor_t flav)
{
switch (flav) {
case FLAV_NS:
return "ns";
case FLAV_MICRODESC:
return "microdesc";
default:
tor_fragile_assert();
return "??";
}
}
/** If <b>question</b> is a string beginning with "ns/" in a format the /** If <b>question</b> is a string beginning with "ns/" in a format the
* control interface expects for a GETINFO question, set *<b>answer</b> to a * control interface expects for a GETINFO question, set *<b>answer</b> to a
* newly-allocated string containing networkstatus lines for the appropriate * newly-allocated string containing networkstatus lines for the appropriate

View File

@ -1641,29 +1641,42 @@ typedef struct vote_routerstatus_t {
vote_microdesc_hash_t *microdesc; vote_microdesc_hash_t *microdesc;
} vote_routerstatus_t; } vote_routerstatus_t;
/** A signature of some document by an authority. */
typedef struct document_signature_t {
/** Declared SHA-1 digest of this voter's identity key */
char identity_digest[DIGEST_LEN];
/** Declared SHA-1 digest of signing key used by this voter. */
char signing_key_digest[DIGEST_LEN];
/** Algorithm used to compute the digest of the document. */
digest_algorithm_t alg;
/** Signature of the signed thing. */
char *signature;
/** Length of <b>signature</b> */
int signature_len;
unsigned int bad_signature : 1; /**< Set to true if we've tried to verify
* the sig, and we know it's bad. */
unsigned int good_signature : 1; /**< Set to true if we've verified the sig
* as good. */
} document_signature_t;
/** Information about a single voter in a vote or a consensus. */ /** Information about a single voter in a vote or a consensus. */
typedef struct networkstatus_voter_info_t { typedef struct networkstatus_voter_info_t {
/** Declared SHA-1 digest of this voter's identity key */
char identity_digest[DIGEST_LEN];
char *nickname; /**< Nickname of this voter */ char *nickname; /**< Nickname of this voter */
char identity_digest[DIGEST_LEN]; /**< Digest of this voter's identity key */ /** Digest of this voter's "legacy" identity key, if any. In vote only; for
* consensuses, we treat legacy keys as additional signers. */
char legacy_id_digest[DIGEST_LEN];
char *address; /**< Address of this voter, in string format. */ char *address; /**< Address of this voter, in string format. */
uint32_t addr; /**< Address of this voter, in IPv4, in host order. */ uint32_t addr; /**< Address of this voter, in IPv4, in host order. */
uint16_t dir_port; /**< Directory port of this voter */ uint16_t dir_port; /**< Directory port of this voter */
uint16_t or_port; /**< OR port of this voter */ uint16_t or_port; /**< OR port of this voter */
char *contact; /**< Contact information for this voter. */ char *contact; /**< Contact information for this voter. */
char vote_digest[DIGEST_LEN]; /**< Digest of this voter's vote, as signed. */ char vote_digest[DIGEST_LEN]; /**< Digest of this voter's vote, as signed. */
/** Digest of this voter's "legacy" identity key, if any. In vote only; for
* consensuses, we treat legacy keys as additional signers. */
char legacy_id_digest[DIGEST_LEN];
/* Nothing from here on is signed. */ /* Nothing from here on is signed. */
char signing_key_digest[DIGEST_LEN]; /**< Declared digest of signing key /** The signature of the document and the signature's status. */
* used by this voter. */ smartlist_t *sigs;
char *signature; /**< Signature from this voter. */
int signature_len; /**< Length of <b>signature</b> */
unsigned int bad_signature : 1; /**< Set to true if we've tried to verify
* the sig, and we know it's bad. */
unsigned int good_signature : 1; /**< Set to true if we've verified the sig
* as good. */
} networkstatus_voter_info_t; } networkstatus_voter_info_t;
/** Enumerates the possible seriousness values of a networkstatus document. */ /** Enumerates the possible seriousness values of a networkstatus document. */
@ -1673,10 +1686,17 @@ typedef enum {
NS_TYPE_OPINION, NS_TYPE_OPINION,
} networkstatus_type_t; } networkstatus_type_t;
/** DOCDOC */
typedef enum {
FLAV_NS,
FLAV_MICRODESC,
} consensus_flavor_t;
/** A common structure to hold a v3 network status vote, or a v3 network /** A common structure to hold a v3 network status vote, or a v3 network
* status consensus. */ * status consensus. */
typedef struct networkstatus_t { typedef struct networkstatus_t {
networkstatus_type_t type; /**< Vote, consensus, or opinion? */ networkstatus_type_t type : 8; /**< Vote, consensus, or opinion? */
consensus_flavor_t flavor : 8; /**< If a consensus, what kind? */
time_t published; /**< Vote only: Time when vote was written. */ time_t published; /**< Vote only: Time when vote was written. */
time_t valid_after; /**< Time after which this vote or consensus applies. */ time_t valid_after; /**< Time after which this vote or consensus applies. */
time_t fresh_until; /**< Time before which this is the most recent vote or time_t fresh_until; /**< Time before which this is the most recent vote or
@ -1715,8 +1735,8 @@ typedef struct networkstatus_t {
struct authority_cert_t *cert; /**< Vote only: the voter's certificate. */ struct authority_cert_t *cert; /**< Vote only: the voter's certificate. */
/** Digest of this document, as signed. */ /** Digests of this document, as signed. */
char networkstatus_digest[DIGEST_LEN]; digests_t digests;
/** List of router statuses, sorted by identity digest. For a vote, /** List of router statuses, sorted by identity digest. For a vote,
* the elements are vote_routerstatus_t; for a consensus, the elements * the elements are vote_routerstatus_t; for a consensus, the elements
@ -1728,14 +1748,15 @@ typedef struct networkstatus_t {
digestmap_t *desc_digest_map; digestmap_t *desc_digest_map;
} networkstatus_t; } networkstatus_t;
/** A set of signatures for a networkstatus consensus. All fields are as for /** A set of signatures for a networkstatus consensus. Unless otherwise
* networkstatus_t. */ * noted, all fields are as for networkstatus_t. */
typedef struct ns_detached_signatures_t { typedef struct ns_detached_signatures_t {
time_t valid_after; time_t valid_after;
time_t fresh_until; time_t fresh_until;
time_t valid_until; time_t valid_until;
char networkstatus_digest[DIGEST_LEN]; strmap_t *digests; /**< Map from flavor name to digestset_t */
smartlist_t *signatures; /* list of networkstatus_voter_info_t */ strmap_t *signatures; /**< Map from flavor name to list of
* document_signature_t */
} ns_detached_signatures_t; } ns_detached_signatures_t;
/** Allowable types of desc_store_t. */ /** Allowable types of desc_store_t. */
@ -3803,12 +3824,6 @@ int dirserv_read_measured_bandwidths(const char *from_file,
void dirvote_free_all(void); void dirvote_free_all(void);
/** DOCDOC */
typedef enum {
FLAV_NS,
FLAV_MICRODESC,
} consensus_flavor_t;
/* vote manipulation */ /* vote manipulation */
char *networkstatus_compute_consensus(smartlist_t *votes, char *networkstatus_compute_consensus(smartlist_t *votes,
int total_authorities, int total_authorities,
@ -3869,6 +3884,9 @@ int dirvote_format_microdesc_vote_line(char *out, size_t out_len,
int vote_routerstatus_find_microdesc_hash(char *digest256_out, int vote_routerstatus_find_microdesc_hash(char *digest256_out,
const vote_routerstatus_t *vrs, const vote_routerstatus_t *vrs,
int method); int method);
document_signature_t *voter_get_sig_by_algorithm(
const networkstatus_voter_info_t *voter,
digest_algorithm_t alg);
#ifdef DIRVOTE_PRIVATE #ifdef DIRVOTE_PRIVATE
char *format_networkstatus_vote(crypto_pk_env_t *private_key, char *format_networkstatus_vote(crypto_pk_env_t *private_key,
@ -4134,9 +4152,9 @@ networkstatus_voter_info_t *networkstatus_get_voter_by_id(
const char *identity); const char *identity);
int networkstatus_check_consensus_signature(networkstatus_t *consensus, int networkstatus_check_consensus_signature(networkstatus_t *consensus,
int warn); int warn);
int networkstatus_check_voter_signature(networkstatus_t *consensus, int networkstatus_check_document_signature(const networkstatus_t *consensus,
networkstatus_voter_info_t *voter, document_signature_t *sig,
authority_cert_t *cert); const authority_cert_t *cert);
char *networkstatus_get_cache_filename(const char *identity_digest); char *networkstatus_get_cache_filename(const char *identity_digest);
int router_set_networkstatus_v2(const char *s, time_t arrived_at, int router_set_networkstatus_v2(const char *s, time_t arrived_at,
v2_networkstatus_source_t source, v2_networkstatus_source_t source,
@ -4189,6 +4207,9 @@ int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name,
int32_t default_val); int32_t default_val);
int getinfo_helper_networkstatus(control_connection_t *conn, int getinfo_helper_networkstatus(control_connection_t *conn,
const char *question, char **answer); const char *question, char **answer);
const char *networkstatus_get_flavor_name(consensus_flavor_t flav);
void document_signature_free(document_signature_t *sig);
document_signature_t *document_signature_dup(const document_signature_t *sig);
void networkstatus_free_all(void); void networkstatus_free_all(void);
/********************************* ntmain.c ***************************/ /********************************* ntmain.c ***************************/
@ -4975,6 +4996,7 @@ int router_get_runningrouters_hash(const char *s, char *digest);
int router_get_networkstatus_v2_hash(const char *s, char *digest); int router_get_networkstatus_v2_hash(const char *s, char *digest);
int router_get_networkstatus_v3_hash(const char *s, char *digest, int router_get_networkstatus_v3_hash(const char *s, char *digest,
digest_algorithm_t algorithm); digest_algorithm_t algorithm);
int router_get_networkstatus_v3_hashes(const char *s, digests_t *digests);
int router_get_extrainfo_hash(const char *s, char *digest); int router_get_extrainfo_hash(const char *s, char *digest);
int router_append_dirobj_signature(char *buf, size_t buf_len, int router_append_dirobj_signature(char *buf, size_t buf_len,
const char *digest, const char *digest,

View File

@ -448,17 +448,18 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
list_pending_downloads(pending, DIR_PURPOSE_FETCH_CERTIFICATE, "fp/"); list_pending_downloads(pending, DIR_PURPOSE_FETCH_CERTIFICATE, "fp/");
if (status) { if (status) {
SMARTLIST_FOREACH(status->voters, networkstatus_voter_info_t *, voter, SMARTLIST_FOREACH_BEGIN(status->voters, networkstatus_voter_info_t *,
{ voter) {
if (tor_digest_is_zero(voter->signing_key_digest)) if (!smartlist_len(voter->sigs))
continue; /* This authority never signed this consensus, so don't continue; /* This authority never signed this consensus, so don't
* go looking for a cert with key digest 0000000000. */ * go looking for a cert with key digest 0000000000. */
if (!cache && if (!cache &&
!trusteddirserver_get_by_v3_auth_digest(voter->identity_digest)) !trusteddirserver_get_by_v3_auth_digest(voter->identity_digest))
continue; /* We are not a cache, and we don't know this authority.*/ continue; /* We are not a cache, and we don't know this authority.*/
cl = get_cert_list(voter->identity_digest); cl = get_cert_list(voter->identity_digest);
SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) {
cert = authority_cert_get_by_digests(voter->identity_digest, cert = authority_cert_get_by_digests(voter->identity_digest,
voter->signing_key_digest); sig->signing_key_digest);
if (cert) { if (cert) {
if (now < cert->expires) if (now < cert->expires)
download_status_reset(&cl->dl_status); download_status_reset(&cl->dl_status);
@ -469,37 +470,36 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
!digestmap_get(pending, voter->identity_digest)) { !digestmap_get(pending, voter->identity_digest)) {
log_notice(LD_DIR, "We're missing a certificate from authority " log_notice(LD_DIR, "We're missing a certificate from authority "
"with signing key %s: launching request.", "with signing key %s: launching request.",
hex_str(voter->signing_key_digest, DIGEST_LEN)); hex_str(sig->signing_key_digest, DIGEST_LEN));
smartlist_add(missing_digests, voter->identity_digest); smartlist_add(missing_digests, sig->identity_digest);
} }
}); } SMARTLIST_FOREACH_END(sig);
} SMARTLIST_FOREACH_END(voter);
} }
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds, SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, trusted_dir_server_t *, ds) {
{ int found = 0;
int found = 0; if (!(ds->type & V3_AUTHORITY))
if (!(ds->type & V3_AUTHORITY)) continue;
continue; if (smartlist_digest_isin(missing_digests, ds->v3_identity_digest))
if (smartlist_digest_isin(missing_digests, ds->v3_identity_digest)) continue;
continue; cl = get_cert_list(ds->v3_identity_digest);
cl = get_cert_list(ds->v3_identity_digest); SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert, {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert, if (!ftime_definitely_after(now, cert->expires)) {
{ /* It's not expired, and we weren't looking for something to
if (!ftime_definitely_after(now, cert->expires)) { * verify a consensus with. Call it done. */
/* It's not expired, and we weren't looking for something to download_status_reset(&cl->dl_status);
* verify a consensus with. Call it done. */ found = 1;
download_status_reset(&cl->dl_status); break;
found = 1;
break;
}
});
if (!found &&
download_status_is_ready(&cl->dl_status, now,MAX_CERT_DL_FAILURES) &&
!digestmap_get(pending, ds->v3_identity_digest)) {
log_notice(LD_DIR, "No current certificate known for authority %s; "
"launching request.", ds->nickname);
smartlist_add(missing_digests, ds->v3_identity_digest);
} }
}); });
if (!found &&
download_status_is_ready(&cl->dl_status, now,MAX_CERT_DL_FAILURES) &&
!digestmap_get(pending, ds->v3_identity_digest)) {
log_notice(LD_DIR, "No current certificate known for authority %s; "
"launching request.", ds->nickname);
smartlist_add(missing_digests, ds->v3_identity_digest);
}
} SMARTLIST_FOREACH_END(ds);
if (!smartlist_len(missing_digests)) { if (!smartlist_len(missing_digests)) {
goto done; goto done;

View File

@ -516,6 +516,9 @@ static int router_get_hash_impl(const char *s, char *digest,
const char *start_str, const char *end_str, const char *start_str, const char *end_str,
char end_char, char end_char,
digest_algorithm_t alg); digest_algorithm_t alg);
static int router_get_hashes_impl(const char *s, digests_t *digests,
const char *start_str, const char *end_str,
char end_char);
static void token_free(directory_token_t *tok); static void token_free(directory_token_t *tok);
static smartlist_t *find_all_exitpolicy(smartlist_t *s); static smartlist_t *find_all_exitpolicy(smartlist_t *s);
static directory_token_t *_find_by_keyword(smartlist_t *s, static directory_token_t *_find_by_keyword(smartlist_t *s,
@ -633,6 +636,16 @@ router_get_networkstatus_v2_hash(const char *s, char *digest)
DIGEST_SHA1); DIGEST_SHA1);
} }
/** DOCDOC */
int
router_get_networkstatus_v3_hashes(const char *s, digests_t *digests)
{
return router_get_hashes_impl(s,digests,
"network-status-version",
"\ndirectory-signature",
' ');
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the network-status /** Set <b>digest</b> to the SHA-1 digest of the hash of the network-status
* string in <b>s</b>. Return 0 on success, -1 on failure. */ * string in <b>s</b>. Return 0 on success, -1 on failure. */
int int
@ -640,7 +653,8 @@ router_get_networkstatus_v3_hash(const char *s, char *digest,
digest_algorithm_t alg) digest_algorithm_t alg)
{ {
return router_get_hash_impl(s,digest, return router_get_hash_impl(s,digest,
"network-status-version","\ndirectory-signature", "network-status-version",
"\ndirectory-signature",
' ', alg); ' ', alg);
} }
@ -2304,7 +2318,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
smartlist_t *rs_tokens = NULL, *footer_tokens = NULL; smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
networkstatus_voter_info_t *voter = NULL; networkstatus_voter_info_t *voter = NULL;
networkstatus_t *ns = NULL; networkstatus_t *ns = NULL;
char ns_digest[DIGEST_LEN]; digests_t ns_digests;
const char *cert, *end_of_header, *end_of_footer, *s_dup = s; const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
directory_token_t *tok; directory_token_t *tok;
int ok; int ok;
@ -2316,7 +2330,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
if (eos_out) if (eos_out)
*eos_out = NULL; *eos_out = NULL;
if (router_get_networkstatus_v3_hash(s, ns_digest, DIGEST_SHA1)) { if (router_get_networkstatus_v3_hashes(s, &ns_digests)) {
log_warn(LD_DIR, "Unable to compute digest of network-status"); log_warn(LD_DIR, "Unable to compute digest of network-status");
goto err; goto err;
} }
@ -2332,7 +2346,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
} }
ns = tor_malloc_zero(sizeof(networkstatus_t)); ns = tor_malloc_zero(sizeof(networkstatus_t));
memcpy(ns->networkstatus_digest, ns_digest, DIGEST_LEN); memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
if (ns_type != NS_TYPE_CONSENSUS) { if (ns_type != NS_TYPE_CONSENSUS) {
const char *end_of_cert = NULL; const char *end_of_cert = NULL;
@ -2486,8 +2500,9 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
if (voter) if (voter)
smartlist_add(ns->voters, voter); smartlist_add(ns->voters, voter);
voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
voter->sigs = smartlist_create();
if (ns->type != NS_TYPE_CONSENSUS) if (ns->type != NS_TYPE_CONSENSUS)
memcpy(voter->vote_digest, ns_digest, DIGEST_LEN); memcpy(voter->vote_digest, ns_digests.d[DIGEST_SHA1], DIGEST_LEN);
voter->nickname = tor_strdup(tok->args[0]); voter->nickname = tor_strdup(tok->args[0]);
if (strlen(tok->args[1]) != HEX_DIGEST_LEN || if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
@ -2622,10 +2637,10 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
goto err; goto err;
} }
SMARTLIST_FOREACH(footer_tokens, directory_token_t *, _tok, SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
{
char declared_identity[DIGEST_LEN]; char declared_identity[DIGEST_LEN];
networkstatus_voter_info_t *v; networkstatus_voter_info_t *v;
document_signature_t *sig;
tok = _tok; tok = _tok;
if (tok->tp != K_DIRECTORY_SIGNATURE) if (tok->tp != K_DIRECTORY_SIGNATURE)
continue; continue;
@ -2650,11 +2665,15 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
"any declared directory source."); "any declared directory source.");
goto err; goto err;
} }
sig = tor_malloc_zero(sizeof(document_signature_t));
memcpy(sig->identity_digest, v->identity_digest, DIGEST_LEN);
sig->alg = DIGEST_SHA1;
if (strlen(tok->args[1]) != HEX_DIGEST_LEN || if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
base16_decode(v->signing_key_digest, sizeof(v->signing_key_digest), base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
tok->args[1], HEX_DIGEST_LEN) < 0) { tok->args[1], HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared digest %s in " log_warn(LD_DIR, "Error decoding declared digest %s in "
"network-status vote.", escaped(tok->args[1])); "network-status vote.", escaped(tok->args[1]));
tor_free(sig);
goto err; goto err;
} }
@ -2663,32 +2682,42 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
DIGEST_LEN)) { DIGEST_LEN)) {
log_warn(LD_DIR, "Digest mismatch between declared and actual on " log_warn(LD_DIR, "Digest mismatch between declared and actual on "
"network-status vote."); "network-status vote.");
tor_free(sig);
goto err; goto err;
} }
} }
if (voter_get_sig_by_algorithm(v, sig->alg)) {
/* We already parsed a vote with this algorithm from this voter. Use the
first one. */
log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
"that contains two votes from the same voter with the same "
"algorithm. Ignoring the second vote.");
tor_free(sig);
continue;
}
if (ns->type != NS_TYPE_CONSENSUS) { if (ns->type != NS_TYPE_CONSENSUS) {
if (check_signature_token(ns_digest, DIGEST_LEN, if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
tok, ns->cert->signing_key, 0, tok, ns->cert->signing_key, 0,
"network-status vote")) "network-status vote")) {
tor_free(sig);
goto err; goto err;
v->good_signature = 1;
} else {
if (tok->object_size >= INT_MAX)
goto err;
/* We already parsed a vote from this voter. Use the first one. */
if (v->signature) {
log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
"that contains two votes from the same voter. Ignoring "
"the second vote.");
continue;
} }
sig->good_signature = 1;
} else {
if (tok->object_size >= INT_MAX) {
tor_free(sig);
goto err;
}
sig->signature = tor_memdup(tok->object_body, tok->object_size);
sig->signature_len = (int) tok->object_size;
v->signature = tor_memdup(tok->object_body, tok->object_size);
v->signature_len = (int) tok->object_size;
} }
smartlist_add(v->sigs, sig);
++n_signatures; ++n_signatures;
}); } SMARTLIST_FOREACH_END(_tok);
if (! n_signatures) { if (! n_signatures) {
log_warn(LD_DIR, "No signatures on networkstatus vote."); log_warn(LD_DIR, "No signatures on networkstatus vote.");
@ -2714,10 +2743,14 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
smartlist_free(tokens); smartlist_free(tokens);
} }
if (voter) { if (voter) {
if (voter->sigs) {
SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
document_signature_free(sig));
smartlist_free(voter->sigs);
}
tor_free(voter->nickname); tor_free(voter->nickname);
tor_free(voter->address); tor_free(voter->address);
tor_free(voter->contact); tor_free(voter->contact);
tor_free(voter->signature);
tor_free(voter); tor_free(voter);
} }
if (rs_tokens) { if (rs_tokens) {
@ -2747,10 +2780,19 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
* networkstatus_parse_vote_from_string(). */ * networkstatus_parse_vote_from_string(). */
directory_token_t *tok; directory_token_t *tok;
memarea_t *area = NULL; memarea_t *area = NULL;
const char *flavor = "ns";
digests_t *digests;
smartlist_t *sig_list;
smartlist_t *tokens = smartlist_create(); smartlist_t *tokens = smartlist_create();
ns_detached_signatures_t *sigs = ns_detached_signatures_t *sigs =
tor_malloc_zero(sizeof(ns_detached_signatures_t)); tor_malloc_zero(sizeof(ns_detached_signatures_t));
sigs->digests = strmap_new();
sigs->signatures = strmap_new();
digests = tor_malloc_zero(sizeof(digests_t));
sig_list = smartlist_create();
strmap_set(sigs->digests, flavor, digests);
strmap_set(sigs->signatures, flavor, sig_list);
if (!eos) if (!eos)
eos = s + strlen(s); eos = s + strlen(s);
@ -2768,7 +2810,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
"networkstatus signatures"); "networkstatus signatures");
goto err; goto err;
} }
if (base16_decode(sigs->networkstatus_digest, DIGEST_LEN, if (base16_decode(digests->d[DIGEST_SHA1], DIGEST_LEN,
tok->args[0], strlen(tok->args[0])) < 0) { tok->args[0], strlen(tok->args[0])) < 0) {
log_warn(LD_DIR, "Bad encoding on on consensus-digest in detached " log_warn(LD_DIR, "Bad encoding on on consensus-digest in detached "
"networkstatus signatures"); "networkstatus signatures");
@ -2793,50 +2835,51 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
goto err; goto err;
} }
sigs->signatures = smartlist_create(); SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
SMARTLIST_FOREACH(tokens, directory_token_t *, _tok, char id_digest[DIGEST_LEN];
{ char sk_digest[DIGEST_LEN];
char id_digest[DIGEST_LEN]; document_signature_t *sig;
char sk_digest[DIGEST_LEN];
networkstatus_voter_info_t *voter;
tok = _tok; tok = _tok;
if (tok->tp != K_DIRECTORY_SIGNATURE) if (tok->tp != K_DIRECTORY_SIGNATURE)
continue; continue;
tor_assert(tok->n_args >= 2); tor_assert(tok->n_args >= 2);
if (!tok->object_type || if (!tok->object_type ||
strcmp(tok->object_type, "SIGNATURE") || strcmp(tok->object_type, "SIGNATURE") ||
tok->object_size < 128 || tok->object_size > 512) { tok->object_size < 128 || tok->object_size > 512) {
log_warn(LD_DIR, "Bad object type or length on directory-signature"); log_warn(LD_DIR, "Bad object type or length on directory-signature");
goto err; goto err;
} }
if (strlen(tok->args[0]) != HEX_DIGEST_LEN || if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
base16_decode(id_digest, sizeof(id_digest), base16_decode(id_digest, sizeof(id_digest),
tok->args[0], HEX_DIGEST_LEN) < 0) { tok->args[0], HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared identity %s in " log_warn(LD_DIR, "Error decoding declared identity %s in "
"network-status vote.", escaped(tok->args[0])); "network-status vote.", escaped(tok->args[0]));
goto err; goto err;
} }
if (strlen(tok->args[1]) != HEX_DIGEST_LEN || if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
base16_decode(sk_digest, sizeof(sk_digest), base16_decode(sk_digest, sizeof(sk_digest),
tok->args[1], HEX_DIGEST_LEN) < 0) { tok->args[1], HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared digest %s in " log_warn(LD_DIR, "Error decoding declared digest %s in "
"network-status vote.", escaped(tok->args[1])); "network-status vote.", escaped(tok->args[1]));
goto err; goto err;
} }
voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); sig = tor_malloc_zero(sizeof(document_signature_t));
memcpy(voter->identity_digest, id_digest, DIGEST_LEN); sig->alg = DIGEST_SHA1;
memcpy(voter->signing_key_digest, sk_digest, DIGEST_LEN); memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
if (tok->object_size >= INT_MAX) memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
goto err; if (tok->object_size >= INT_MAX) {
voter->signature = tor_memdup(tok->object_body, tok->object_size); tor_free(sig);
voter->signature_len = (int) tok->object_size; goto err;
}
sig->signature = tor_memdup(tok->object_body, tok->object_size);
sig->signature_len = (int) tok->object_size;
smartlist_add(sigs->signatures, voter); smartlist_add(sig_list, sig);
}); } SMARTLIST_FOREACH_END(_tok);
goto done; goto done;
err: err:
@ -3428,18 +3471,11 @@ find_all_exitpolicy(smartlist_t *s)
return out; return out;
} }
/** Compute the digest of the substring of <b>s</b> taken from the first
* occurrence of <b>start_str</b> through the first instance of c after the
* first subsequent occurrence of <b>end_str</b>; store the 20-byte result in
* <b>digest</b>; return 0 on success.
*
* If no such substring exists, return -1.
*/
static int static int
router_get_hash_impl(const char *s, char *digest, router_get_hash_impl_helper(const char *s,
const char *start_str, const char *start_str,
const char *end_str, char end_c, const char *end_str, char end_c,
digest_algorithm_t alg) const char **start_out, const char **end_out)
{ {
char *start, *end; char *start, *end;
start = strstr(s, start_str); start = strstr(s, start_str);
@ -3465,6 +3501,28 @@ router_get_hash_impl(const char *s, char *digest,
} }
++end; ++end;
*start_out = start;
*end_out = end;
return 0;
}
/** Compute the digest of the substring of <b>s</b> taken from the first
* occurrence of <b>start_str</b> through the first instance of c after the
* first subsequent occurrence of <b>end_str</b>; store the 20-byte result in
* <b>digest</b>; return 0 on success.
*
* If no such substring exists, return -1.
*/
static int
router_get_hash_impl(const char *s, char *digest,
const char *start_str,
const char *end_str, char end_c,
digest_algorithm_t alg)
{
const char *start=NULL, *end=NULL;
if (router_get_hash_impl_helper(s,start_str,end_str,end_c,&start,&end)<0)
return -1;
if (alg == DIGEST_SHA1) { if (alg == DIGEST_SHA1) {
if (crypto_digest(digest, start, end-start)) { if (crypto_digest(digest, start, end-start)) {
log_warn(LD_BUG,"couldn't compute digest"); log_warn(LD_BUG,"couldn't compute digest");
@ -3480,6 +3538,24 @@ router_get_hash_impl(const char *s, char *digest,
return 0; return 0;
} }
/** As router_get_hash_impl, but compute all hashes. */
static int
router_get_hashes_impl(const char *s, digests_t *digests,
const char *start_str,
const char *end_str, char end_c)
{
const char *start=NULL, *end=NULL;
if (router_get_hash_impl_helper(s,start_str,end_str,end_c,&start,&end)<0)
return -1;
if (crypto_digest_all(digests, start, end-start)) {
log_warn(LD_BUG,"couldn't compute digests");
return -1;
}
return 0;
}
/** DOCDOC Assuming that s starts with a microdesc, return the start of the /** DOCDOC Assuming that s starts with a microdesc, return the start of the
* *NEXT* one. */ * *NEXT* one. */
static const char * static const char *

View File

@ -571,6 +571,7 @@ test_dir_v3_networkstatus(void)
time_t now = time(NULL); time_t now = time(NULL);
networkstatus_voter_info_t *voter; networkstatus_voter_info_t *voter;
document_signature_t *sig;
networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL; networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL;
vote_routerstatus_t *vrs; vote_routerstatus_t *vrs;
routerstatus_t *rs; routerstatus_t *rs;
@ -946,20 +947,25 @@ test_dir_v3_networkstatus(void)
/* Check signatures. the first voter is a pseudo-entry with a legacy key. /* Check signatures. the first voter is a pseudo-entry with a legacy key.
* The second one hasn't signed. The fourth one has signed: validate it. */ * The second one hasn't signed. The fourth one has signed: validate it. */
voter = smartlist_get(con->voters, 1); voter = smartlist_get(con->voters, 1);
test_assert(!voter->signature); test_eq(smartlist_len(voter->sigs), 0);
test_assert(!voter->good_signature); #if 0
test_assert(!voter->bad_signature); sig = smartlist_get(voter->sigs, 1);
test_assert(!sig->signature);
test_assert(!sig->good_signature);
test_assert(!sig->bad_signature);
#endif
voter = smartlist_get(con->voters, 3); voter = smartlist_get(con->voters, 3);
test_assert(voter->signature); test_eq(smartlist_len(voter->sigs), 1);
test_assert(!voter->good_signature); sig = smartlist_get(voter->sigs, 0);
test_assert(!voter->bad_signature); test_assert(sig->signature);
test_assert(!networkstatus_check_voter_signature(con, test_assert(!sig->good_signature);
smartlist_get(con->voters, 3), test_assert(!sig->bad_signature);
cert3));
test_assert(voter->signature); test_assert(!networkstatus_check_document_signature(con, sig, cert3));
test_assert(voter->good_signature); test_assert(sig->signature);
test_assert(!voter->bad_signature); test_assert(sig->good_signature);
test_assert(!sig->bad_signature);
{ {
const char *msg=NULL; const char *msg=NULL;
@ -984,10 +990,8 @@ test_dir_v3_networkstatus(void)
test_assert(con3); test_assert(con3);
/* All three should have the same digest. */ /* All three should have the same digest. */
test_memeq(con->networkstatus_digest, con2->networkstatus_digest, test_memeq(&con->digests, &con2->digests, sizeof(digests_t));
DIGEST_LEN); test_memeq(&con->digests, &con3->digests, sizeof(digests_t));
test_memeq(con->networkstatus_digest, con3->networkstatus_digest,
DIGEST_LEN);
/* Extract a detached signature from con3. */ /* Extract a detached signature from con3. */
detached_text1 = networkstatus_get_detached_signatures(con3); detached_text1 = networkstatus_get_detached_signatures(con3);
@ -1000,12 +1004,20 @@ test_dir_v3_networkstatus(void)
test_eq(dsig1->valid_after, con3->valid_after); test_eq(dsig1->valid_after, con3->valid_after);
test_eq(dsig1->fresh_until, con3->fresh_until); test_eq(dsig1->fresh_until, con3->fresh_until);
test_eq(dsig1->valid_until, con3->valid_until); test_eq(dsig1->valid_until, con3->valid_until);
test_memeq(dsig1->networkstatus_digest, con3->networkstatus_digest, {
DIGEST_LEN); digests_t *dsig_digests = strmap_get(dsig1->digests, "ns");
test_eq(1, smartlist_len(dsig1->signatures)); test_assert(dsig_digests);
voter = smartlist_get(dsig1->signatures, 0); test_memeq(dsig_digests->d[DIGEST_SHA1], con3->digests.d[DIGEST_SHA1],
test_memeq(voter->identity_digest, cert1->cache_info.identity_digest, DIGEST_LEN);
DIGEST_LEN); }
{
smartlist_t *dsig_signatures = strmap_get(dsig1->signatures, "ns");
test_assert(dsig_signatures);
test_eq(1, smartlist_len(dsig_signatures));
sig = smartlist_get(dsig_signatures, 0);
test_memeq(sig->identity_digest, cert1->cache_info.identity_digest,
DIGEST_LEN);
}
/* Try adding it to con2. */ /* Try adding it to con2. */
detached_text2 = networkstatus_get_detached_signatures(con2); detached_text2 = networkstatus_get_detached_signatures(con2);
@ -1023,7 +1035,8 @@ test_dir_v3_networkstatus(void)
printf("%s\n", hd); printf("%s\n", hd);
}); });
*/ */
test_eq(2, smartlist_len(dsig2->signatures)); test_eq(2,
smartlist_len((smartlist_t*)strmap_get(dsig2->signatures, "ns")));
/* Try adding to con2 twice; verify that nothing changes. */ /* Try adding to con2 twice; verify that nothing changes. */
test_eq(0, networkstatus_add_detached_signatures(con2, dsig1, &msg)); test_eq(0, networkstatus_add_detached_signatures(con2, dsig1, &msg));
@ -1031,13 +1044,14 @@ test_dir_v3_networkstatus(void)
/* Add to con. */ /* Add to con. */
test_eq(2, networkstatus_add_detached_signatures(con, dsig2, &msg)); test_eq(2, networkstatus_add_detached_signatures(con, dsig2, &msg));
/* Check signatures */ /* Check signatures */
test_assert(!networkstatus_check_voter_signature(con, voter = smartlist_get(con->voters, 1);
smartlist_get(con->voters, 1), sig = smartlist_get(voter->sigs, 0);
cert2)); test_assert(sig);
test_assert(!networkstatus_check_voter_signature(con, test_assert(!networkstatus_check_document_signature(con, sig, cert2));
smartlist_get(con->voters, 2), voter = smartlist_get(con->voters, 2);
cert1)); sig = smartlist_get(voter->sigs, 0);
test_assert(sig);
test_assert(!networkstatus_check_document_signature(con, sig, cert1));
} }
done: done: