mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 12:23:32 +01:00
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:
parent
e1ddee8bbe
commit
3b2fc659a8
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
171
src/or/dirvote.c
171
src/or/dirvote.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +279,25 @@ networkstatus_v2_free(networkstatus_v2_t *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
|
||||
networkstatus_vote_free(networkstatus_t *ns)
|
||||
{
|
||||
@ -301,14 +319,17 @@ networkstatus_vote_free(networkstatus_t *ns)
|
||||
smartlist_free(ns->supported_methods);
|
||||
}
|
||||
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->address);
|
||||
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);
|
||||
});
|
||||
} SMARTLIST_FOREACH_END(voter);
|
||||
smartlist_free(ns->voters);
|
||||
}
|
||||
if (ns->cert)
|
||||
@ -347,34 +368,38 @@ networkstatus_get_voter_by_id(networkstatus_t *vote,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Check whether the signature on <b>voter</b> is correctly signed by
|
||||
* the signing key of <b>cert</b>. Return -1 if <b>cert</b> doesn't match the
|
||||
/** Check whether the signature <b>sig</b> is correctly signed with 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
|
||||
* <b>voter</b>, and return 0. */
|
||||
/* (private; exposed for testing.) */
|
||||
int
|
||||
networkstatus_check_voter_signature(networkstatus_t *consensus,
|
||||
networkstatus_voter_info_t *voter,
|
||||
authority_cert_t *cert)
|
||||
networkstatus_check_document_signature(const networkstatus_t *consensus,
|
||||
document_signature_t *sig,
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
|
||||
signed_digest_len = crypto_pk_keysize(cert->signing_key);
|
||||
signed_digest = tor_malloc(signed_digest_len);
|
||||
if (crypto_pk_public_checksig(cert->signing_key,
|
||||
signed_digest,
|
||||
voter->signature,
|
||||
voter->signature_len) != DIGEST_LEN ||
|
||||
memcmp(signed_digest, consensus->networkstatus_digest, DIGEST_LEN)) {
|
||||
sig->signature,
|
||||
sig->signature_len) < dlen ||
|
||||
memcmp(signed_digest, consensus->digests.d[sig->alg], dlen)) {
|
||||
log_warn(LD_DIR, "Got a bad signature on a networkstatus vote");
|
||||
voter->bad_signature = 1;
|
||||
sig->bad_signature = 1;
|
||||
} else {
|
||||
voter->good_signature = 1;
|
||||
sig->good_signature = 1;
|
||||
}
|
||||
tor_free(signed_digest);
|
||||
return 0;
|
||||
@ -407,37 +432,52 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
|
||||
|
||||
tor_assert(consensus->type == NS_TYPE_CONSENSUS);
|
||||
|
||||
SMARTLIST_FOREACH(consensus->voters, networkstatus_voter_info_t *, voter,
|
||||
{
|
||||
if (!voter->good_signature && !voter->bad_signature && voter->signature) {
|
||||
SMARTLIST_FOREACH_BEGIN(consensus->voters, networkstatus_voter_info_t *,
|
||||
voter) {
|
||||
int good_here = 0;
|
||||
int bad_here = 0;
|
||||
int missing_key_here = 0;
|
||||
SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) {
|
||||
if (!sig->good_signature && !sig->bad_signature &&
|
||||
sig->signature) {
|
||||
/* we can try to check the signature. */
|
||||
int is_v3_auth = trusteddirserver_get_by_v3_auth_digest(
|
||||
voter->identity_digest) != NULL;
|
||||
sig->identity_digest) != NULL;
|
||||
authority_cert_t *cert =
|
||||
authority_cert_get_by_digests(voter->identity_digest,
|
||||
voter->signing_key_digest);
|
||||
authority_cert_get_by_digests(sig->identity_digest,
|
||||
sig->signing_key_digest);
|
||||
tor_assert(!memcmp(sig->identity_digest, voter->identity_digest,
|
||||
DIGEST_LEN));
|
||||
|
||||
if (!is_v3_auth) {
|
||||
smartlist_add(unrecognized, voter);
|
||||
++n_unknown;
|
||||
continue;
|
||||
} else if (!cert || cert->expires < now) {
|
||||
smartlist_add(need_certs_from, voter);
|
||||
++n_missing_key;
|
||||
++missing_key_here;
|
||||
continue;
|
||||
}
|
||||
if (networkstatus_check_voter_signature(consensus, voter, cert) < 0) {
|
||||
if (networkstatus_check_document_signature(consensus, sig, cert) < 0) {
|
||||
smartlist_add(need_certs_from, voter);
|
||||
++n_missing_key;
|
||||
++missing_key_here;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (voter->good_signature)
|
||||
if (sig->good_signature)
|
||||
++good_here;
|
||||
else if (sig->bad_signature)
|
||||
++bad_here;
|
||||
} SMARTLIST_FOREACH_END(sig);
|
||||
if (good_here)
|
||||
++n_good;
|
||||
else if (voter->bad_signature)
|
||||
else if (bad_here)
|
||||
++n_bad;
|
||||
else if (missing_key_here)
|
||||
++n_missing_key;
|
||||
else
|
||||
++n_no_signature;
|
||||
});
|
||||
} SMARTLIST_FOREACH_END(voter);
|
||||
|
||||
/* Now see whether we're missing any voters entirely. */
|
||||
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
|
||||
@ -1434,8 +1474,7 @@ networkstatus_set_current_consensus(const char *consensus, unsigned flags)
|
||||
}
|
||||
|
||||
if (current_consensus &&
|
||||
!memcmp(c->networkstatus_digest, current_consensus->networkstatus_digest,
|
||||
DIGEST_LEN)) {
|
||||
!memcmp(&c->digests, ¤t_consensus->digests, sizeof(c->digests))) {
|
||||
/* We already have this one. That's a failure. */
|
||||
log_info(LD_DIR, "Got a consensus we already have");
|
||||
goto done;
|
||||
@ -1669,10 +1708,8 @@ download_status_map_update_from_v2_networkstatus(void)
|
||||
v2_download_status_map = digestmap_new();
|
||||
|
||||
dl_status = digestmap_new();
|
||||
SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns,
|
||||
{
|
||||
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
|
||||
{
|
||||
SMARTLIST_FOREACH_BEGIN(networkstatus_v2_list, networkstatus_v2_t *, ns) {
|
||||
SMARTLIST_FOREACH_BEGIN(ns->entries, routerstatus_t *, rs) {
|
||||
const char *d = rs->descriptor_digest;
|
||||
download_status_t *s;
|
||||
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));
|
||||
}
|
||||
digestmap_set(dl_status, d, s);
|
||||
});
|
||||
});
|
||||
} SMARTLIST_FOREACH_END(rs);
|
||||
} SMARTLIST_FOREACH_END(ns);
|
||||
digestmap_free(v2_download_status_map, _tor_free);
|
||||
v2_download_status_map = dl_status;
|
||||
networkstatus_v2_list_has_changed = 0;
|
||||
@ -1930,6 +1967,22 @@ networkstatus_get_param(networkstatus_t *ns, const char *param_name,
|
||||
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
|
||||
* control interface expects for a GETINFO question, set *<b>answer</b> to a
|
||||
* newly-allocated string containing networkstatus lines for the appropriate
|
||||
|
78
src/or/or.h
78
src/or/or.h
@ -1641,29 +1641,42 @@ typedef struct vote_routerstatus_t {
|
||||
vote_microdesc_hash_t *microdesc;
|
||||
} 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. */
|
||||
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 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. */
|
||||
uint32_t addr; /**< Address of this voter, in IPv4, in host order. */
|
||||
uint16_t dir_port; /**< Directory port of this voter */
|
||||
uint16_t or_port; /**< OR port of this voter */
|
||||
char *contact; /**< Contact information for this voter. */
|
||||
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. */
|
||||
char signing_key_digest[DIGEST_LEN]; /**< Declared digest of signing key
|
||||
* used by this voter. */
|
||||
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. */
|
||||
/** The signature of the document and the signature's status. */
|
||||
smartlist_t *sigs;
|
||||
} networkstatus_voter_info_t;
|
||||
|
||||
/** Enumerates the possible seriousness values of a networkstatus document. */
|
||||
@ -1673,10 +1686,17 @@ typedef enum {
|
||||
NS_TYPE_OPINION,
|
||||
} 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
|
||||
* status consensus. */
|
||||
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 valid_after; /**< Time after which this vote or consensus applies. */
|
||||
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. */
|
||||
|
||||
/** Digest of this document, as signed. */
|
||||
char networkstatus_digest[DIGEST_LEN];
|
||||
/** Digests of this document, as signed. */
|
||||
digests_t digests;
|
||||
|
||||
/** List of router statuses, sorted by identity digest. For a vote,
|
||||
* the elements are vote_routerstatus_t; for a consensus, the elements
|
||||
@ -1728,14 +1748,15 @@ typedef struct networkstatus_t {
|
||||
digestmap_t *desc_digest_map;
|
||||
} networkstatus_t;
|
||||
|
||||
/** A set of signatures for a networkstatus consensus. All fields are as for
|
||||
* networkstatus_t. */
|
||||
/** A set of signatures for a networkstatus consensus. Unless otherwise
|
||||
* noted, all fields are as for networkstatus_t. */
|
||||
typedef struct ns_detached_signatures_t {
|
||||
time_t valid_after;
|
||||
time_t fresh_until;
|
||||
time_t valid_until;
|
||||
char networkstatus_digest[DIGEST_LEN];
|
||||
smartlist_t *signatures; /* list of networkstatus_voter_info_t */
|
||||
strmap_t *digests; /**< Map from flavor name to digestset_t */
|
||||
strmap_t *signatures; /**< Map from flavor name to list of
|
||||
* document_signature_t */
|
||||
} ns_detached_signatures_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);
|
||||
|
||||
/** DOCDOC */
|
||||
typedef enum {
|
||||
FLAV_NS,
|
||||
FLAV_MICRODESC,
|
||||
} consensus_flavor_t;
|
||||
|
||||
/* vote manipulation */
|
||||
char *networkstatus_compute_consensus(smartlist_t *votes,
|
||||
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,
|
||||
const vote_routerstatus_t *vrs,
|
||||
int method);
|
||||
document_signature_t *voter_get_sig_by_algorithm(
|
||||
const networkstatus_voter_info_t *voter,
|
||||
digest_algorithm_t alg);
|
||||
|
||||
#ifdef DIRVOTE_PRIVATE
|
||||
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);
|
||||
int networkstatus_check_consensus_signature(networkstatus_t *consensus,
|
||||
int warn);
|
||||
int networkstatus_check_voter_signature(networkstatus_t *consensus,
|
||||
networkstatus_voter_info_t *voter,
|
||||
authority_cert_t *cert);
|
||||
int networkstatus_check_document_signature(const networkstatus_t *consensus,
|
||||
document_signature_t *sig,
|
||||
const authority_cert_t *cert);
|
||||
char *networkstatus_get_cache_filename(const char *identity_digest);
|
||||
int router_set_networkstatus_v2(const char *s, time_t arrived_at,
|
||||
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);
|
||||
int getinfo_helper_networkstatus(control_connection_t *conn,
|
||||
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);
|
||||
|
||||
/********************************* 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_v3_hash(const char *s, char *digest,
|
||||
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_append_dirobj_signature(char *buf, size_t buf_len,
|
||||
const char *digest,
|
||||
|
@ -448,17 +448,18 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
|
||||
|
||||
list_pending_downloads(pending, DIR_PURPOSE_FETCH_CERTIFICATE, "fp/");
|
||||
if (status) {
|
||||
SMARTLIST_FOREACH(status->voters, networkstatus_voter_info_t *, voter,
|
||||
{
|
||||
if (tor_digest_is_zero(voter->signing_key_digest))
|
||||
SMARTLIST_FOREACH_BEGIN(status->voters, networkstatus_voter_info_t *,
|
||||
voter) {
|
||||
if (!smartlist_len(voter->sigs))
|
||||
continue; /* This authority never signed this consensus, so don't
|
||||
* go looking for a cert with key digest 0000000000. */
|
||||
if (!cache &&
|
||||
!trusteddirserver_get_by_v3_auth_digest(voter->identity_digest))
|
||||
continue; /* We are not a cache, and we don't know this authority.*/
|
||||
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,
|
||||
voter->signing_key_digest);
|
||||
sig->signing_key_digest);
|
||||
if (cert) {
|
||||
if (now < cert->expires)
|
||||
download_status_reset(&cl->dl_status);
|
||||
@ -469,21 +470,20 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
|
||||
!digestmap_get(pending, voter->identity_digest)) {
|
||||
log_notice(LD_DIR, "We're missing a certificate from authority "
|
||||
"with signing key %s: launching request.",
|
||||
hex_str(voter->signing_key_digest, DIGEST_LEN));
|
||||
smartlist_add(missing_digests, voter->identity_digest);
|
||||
hex_str(sig->signing_key_digest, DIGEST_LEN));
|
||||
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;
|
||||
if (!(ds->type & V3_AUTHORITY))
|
||||
continue;
|
||||
if (smartlist_digest_isin(missing_digests, ds->v3_identity_digest))
|
||||
continue;
|
||||
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
|
||||
* verify a consensus with. Call it done. */
|
||||
@ -499,7 +499,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
|
||||
"launching request.", ds->nickname);
|
||||
smartlist_add(missing_digests, ds->v3_identity_digest);
|
||||
}
|
||||
});
|
||||
} SMARTLIST_FOREACH_END(ds);
|
||||
|
||||
if (!smartlist_len(missing_digests)) {
|
||||
goto done;
|
||||
|
@ -516,6 +516,9 @@ static int router_get_hash_impl(const char *s, char *digest,
|
||||
const char *start_str, const char *end_str,
|
||||
char end_char,
|
||||
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 smartlist_t *find_all_exitpolicy(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);
|
||||
}
|
||||
|
||||
/** 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
|
||||
* string in <b>s</b>. Return 0 on success, -1 on failure. */
|
||||
int
|
||||
@ -640,7 +653,8 @@ router_get_networkstatus_v3_hash(const char *s, char *digest,
|
||||
digest_algorithm_t alg)
|
||||
{
|
||||
return router_get_hash_impl(s,digest,
|
||||
"network-status-version","\ndirectory-signature",
|
||||
"network-status-version",
|
||||
"\ndirectory-signature",
|
||||
' ', 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;
|
||||
networkstatus_voter_info_t *voter = 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;
|
||||
directory_token_t *tok;
|
||||
int ok;
|
||||
@ -2316,7 +2330,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
||||
if (eos_out)
|
||||
*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");
|
||||
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));
|
||||
memcpy(ns->networkstatus_digest, ns_digest, DIGEST_LEN);
|
||||
memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
|
||||
|
||||
if (ns_type != NS_TYPE_CONSENSUS) {
|
||||
const char *end_of_cert = NULL;
|
||||
@ -2486,8 +2500,9 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
||||
if (voter)
|
||||
smartlist_add(ns->voters, voter);
|
||||
voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
|
||||
voter->sigs = smartlist_create();
|
||||
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]);
|
||||
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;
|
||||
}
|
||||
|
||||
SMARTLIST_FOREACH(footer_tokens, directory_token_t *, _tok,
|
||||
{
|
||||
SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
|
||||
char declared_identity[DIGEST_LEN];
|
||||
networkstatus_voter_info_t *v;
|
||||
document_signature_t *sig;
|
||||
tok = _tok;
|
||||
if (tok->tp != K_DIRECTORY_SIGNATURE)
|
||||
continue;
|
||||
@ -2650,11 +2665,15 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
||||
"any declared directory source.");
|
||||
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 ||
|
||||
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) {
|
||||
log_warn(LD_DIR, "Error decoding declared digest %s in "
|
||||
"network-status vote.", escaped(tok->args[1]));
|
||||
tor_free(sig);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -2663,32 +2682,42 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
||||
DIGEST_LEN)) {
|
||||
log_warn(LD_DIR, "Digest mismatch between declared and actual on "
|
||||
"network-status vote.");
|
||||
tor_free(sig);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (ns->type != NS_TYPE_CONSENSUS) {
|
||||
if (check_signature_token(ns_digest, DIGEST_LEN,
|
||||
tok, ns->cert->signing_key, 0,
|
||||
"network-status vote"))
|
||||
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) {
|
||||
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. Ignoring "
|
||||
"the second vote.");
|
||||
"that contains two votes from the same voter with the same "
|
||||
"algorithm. Ignoring the second vote.");
|
||||
tor_free(sig);
|
||||
continue;
|
||||
}
|
||||
|
||||
v->signature = tor_memdup(tok->object_body, tok->object_size);
|
||||
v->signature_len = (int) tok->object_size;
|
||||
if (ns->type != NS_TYPE_CONSENSUS) {
|
||||
if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
|
||||
tok, ns->cert->signing_key, 0,
|
||||
"network-status vote")) {
|
||||
tor_free(sig);
|
||||
goto err;
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
smartlist_add(v->sigs, sig);
|
||||
|
||||
++n_signatures;
|
||||
});
|
||||
} SMARTLIST_FOREACH_END(_tok);
|
||||
|
||||
if (! n_signatures) {
|
||||
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);
|
||||
}
|
||||
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->address);
|
||||
tor_free(voter->contact);
|
||||
tor_free(voter->signature);
|
||||
tor_free(voter);
|
||||
}
|
||||
if (rs_tokens) {
|
||||
@ -2747,10 +2780,19 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
|
||||
* networkstatus_parse_vote_from_string(). */
|
||||
directory_token_t *tok;
|
||||
memarea_t *area = NULL;
|
||||
const char *flavor = "ns";
|
||||
digests_t *digests;
|
||||
smartlist_t *sig_list;
|
||||
|
||||
smartlist_t *tokens = smartlist_create();
|
||||
ns_detached_signatures_t *sigs =
|
||||
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)
|
||||
eos = s + strlen(s);
|
||||
@ -2768,7 +2810,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
|
||||
"networkstatus signatures");
|
||||
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) {
|
||||
log_warn(LD_DIR, "Bad encoding on on consensus-digest in detached "
|
||||
"networkstatus signatures");
|
||||
@ -2793,12 +2835,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
|
||||
goto err;
|
||||
}
|
||||
|
||||
sigs->signatures = smartlist_create();
|
||||
SMARTLIST_FOREACH(tokens, directory_token_t *, _tok,
|
||||
{
|
||||
SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
|
||||
char id_digest[DIGEST_LEN];
|
||||
char sk_digest[DIGEST_LEN];
|
||||
networkstatus_voter_info_t *voter;
|
||||
document_signature_t *sig;
|
||||
|
||||
tok = _tok;
|
||||
if (tok->tp != K_DIRECTORY_SIGNATURE)
|
||||
@ -2827,16 +2867,19 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
|
||||
goto err;
|
||||
}
|
||||
|
||||
voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
|
||||
memcpy(voter->identity_digest, id_digest, DIGEST_LEN);
|
||||
memcpy(voter->signing_key_digest, sk_digest, DIGEST_LEN);
|
||||
if (tok->object_size >= INT_MAX)
|
||||
sig = tor_malloc_zero(sizeof(document_signature_t));
|
||||
sig->alg = DIGEST_SHA1;
|
||||
memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
|
||||
memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
|
||||
if (tok->object_size >= INT_MAX) {
|
||||
tor_free(sig);
|
||||
goto err;
|
||||
voter->signature = tor_memdup(tok->object_body, tok->object_size);
|
||||
voter->signature_len = (int) tok->object_size;
|
||||
}
|
||||
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;
|
||||
err:
|
||||
@ -3428,18 +3471,11 @@ find_all_exitpolicy(smartlist_t *s)
|
||||
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
|
||||
router_get_hash_impl(const char *s, char *digest,
|
||||
router_get_hash_impl_helper(const char *s,
|
||||
const char *start_str,
|
||||
const char *end_str, char end_c,
|
||||
digest_algorithm_t alg)
|
||||
const char **start_out, const char **end_out)
|
||||
{
|
||||
char *start, *end;
|
||||
start = strstr(s, start_str);
|
||||
@ -3465,6 +3501,28 @@ router_get_hash_impl(const char *s, char *digest,
|
||||
}
|
||||
++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 (crypto_digest(digest, start, end-start)) {
|
||||
log_warn(LD_BUG,"couldn't compute digest");
|
||||
@ -3480,6 +3538,24 @@ router_get_hash_impl(const char *s, char *digest,
|
||||
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
|
||||
* *NEXT* one. */
|
||||
static const char *
|
||||
|
@ -571,6 +571,7 @@ test_dir_v3_networkstatus(void)
|
||||
|
||||
time_t now = time(NULL);
|
||||
networkstatus_voter_info_t *voter;
|
||||
document_signature_t *sig;
|
||||
networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL;
|
||||
vote_routerstatus_t *vrs;
|
||||
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.
|
||||
* The second one hasn't signed. The fourth one has signed: validate it. */
|
||||
voter = smartlist_get(con->voters, 1);
|
||||
test_assert(!voter->signature);
|
||||
test_assert(!voter->good_signature);
|
||||
test_assert(!voter->bad_signature);
|
||||
test_eq(smartlist_len(voter->sigs), 0);
|
||||
#if 0
|
||||
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);
|
||||
test_assert(voter->signature);
|
||||
test_assert(!voter->good_signature);
|
||||
test_assert(!voter->bad_signature);
|
||||
test_assert(!networkstatus_check_voter_signature(con,
|
||||
smartlist_get(con->voters, 3),
|
||||
cert3));
|
||||
test_assert(voter->signature);
|
||||
test_assert(voter->good_signature);
|
||||
test_assert(!voter->bad_signature);
|
||||
test_eq(smartlist_len(voter->sigs), 1);
|
||||
sig = smartlist_get(voter->sigs, 0);
|
||||
test_assert(sig->signature);
|
||||
test_assert(!sig->good_signature);
|
||||
test_assert(!sig->bad_signature);
|
||||
|
||||
test_assert(!networkstatus_check_document_signature(con, sig, cert3));
|
||||
test_assert(sig->signature);
|
||||
test_assert(sig->good_signature);
|
||||
test_assert(!sig->bad_signature);
|
||||
|
||||
{
|
||||
const char *msg=NULL;
|
||||
@ -984,10 +990,8 @@ test_dir_v3_networkstatus(void)
|
||||
test_assert(con3);
|
||||
|
||||
/* All three should have the same digest. */
|
||||
test_memeq(con->networkstatus_digest, con2->networkstatus_digest,
|
||||
DIGEST_LEN);
|
||||
test_memeq(con->networkstatus_digest, con3->networkstatus_digest,
|
||||
DIGEST_LEN);
|
||||
test_memeq(&con->digests, &con2->digests, sizeof(digests_t));
|
||||
test_memeq(&con->digests, &con3->digests, sizeof(digests_t));
|
||||
|
||||
/* Extract a detached signature from 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->fresh_until, con3->fresh_until);
|
||||
test_eq(dsig1->valid_until, con3->valid_until);
|
||||
test_memeq(dsig1->networkstatus_digest, con3->networkstatus_digest,
|
||||
{
|
||||
digests_t *dsig_digests = strmap_get(dsig1->digests, "ns");
|
||||
test_assert(dsig_digests);
|
||||
test_memeq(dsig_digests->d[DIGEST_SHA1], con3->digests.d[DIGEST_SHA1],
|
||||
DIGEST_LEN);
|
||||
test_eq(1, smartlist_len(dsig1->signatures));
|
||||
voter = smartlist_get(dsig1->signatures, 0);
|
||||
test_memeq(voter->identity_digest, cert1->cache_info.identity_digest,
|
||||
}
|
||||
{
|
||||
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. */
|
||||
detached_text2 = networkstatus_get_detached_signatures(con2);
|
||||
@ -1023,7 +1035,8 @@ test_dir_v3_networkstatus(void)
|
||||
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. */
|
||||
test_eq(0, networkstatus_add_detached_signatures(con2, dsig1, &msg));
|
||||
@ -1031,13 +1044,14 @@ test_dir_v3_networkstatus(void)
|
||||
/* Add to con. */
|
||||
test_eq(2, networkstatus_add_detached_signatures(con, dsig2, &msg));
|
||||
/* Check signatures */
|
||||
test_assert(!networkstatus_check_voter_signature(con,
|
||||
smartlist_get(con->voters, 1),
|
||||
cert2));
|
||||
test_assert(!networkstatus_check_voter_signature(con,
|
||||
smartlist_get(con->voters, 2),
|
||||
cert1));
|
||||
|
||||
voter = smartlist_get(con->voters, 1);
|
||||
sig = smartlist_get(voter->sigs, 0);
|
||||
test_assert(sig);
|
||||
test_assert(!networkstatus_check_document_signature(con, sig, cert2));
|
||||
voter = smartlist_get(con->voters, 2);
|
||||
sig = smartlist_get(voter->sigs, 0);
|
||||
test_assert(sig);
|
||||
test_assert(!networkstatus_check_document_signature(con, sig, cert1));
|
||||
}
|
||||
|
||||
done:
|
||||
|
Loading…
Reference in New Issue
Block a user