Parse detached signatures and microdesc networkstatuses correctly.

This commit is contained in:
Nick Mathewson 2009-09-23 15:23:04 -04:00
parent 0bce0161dd
commit a19981725d
4 changed files with 170 additions and 45 deletions

View File

@ -1983,6 +1983,18 @@ networkstatus_get_flavor_name(consensus_flavor_t flav)
}
}
/** DOCDOC return -1 on unknown */
int
networkstatus_parse_flavor_name(const char *flavname)
{
if (!strcmp(flavname, "ns"))
return FLAV_NS;
else if (!strcmp(flavname, "microdesc"))
return FLAV_MICRODESC;
else
return -1;
}
/** 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

View File

@ -4211,6 +4211,7 @@ int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name,
int getinfo_helper_networkstatus(control_connection_t *conn,
const char *question, char **answer);
const char *networkstatus_get_flavor_name(consensus_flavor_t flav);
int networkstatus_parse_flavor_name(const char *flavname);
void document_signature_free(document_signature_t *sig);
document_signature_t *document_signature_dup(const document_signature_t *sig);
void networkstatus_free_all(void);

View File

@ -321,7 +321,7 @@ static token_rule_t extrainfo_token_table[] = {
* documents. */
static token_rule_t rtrstatus_token_table[] = {
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
T1( "r", K_R, GE(8), NO_OBJ ),
T1( "r", K_R, GE(7), NO_OBJ ),
T1( "s", K_S, ARGS, NO_OBJ ),
T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
T01("w", K_W, ARGS, NO_OBJ ),
@ -1900,22 +1900,29 @@ find_start_of_next_routerstatus(const char *s)
* If <b>consensus_method</b> is nonzero, this routerstatus is part of a
* consensus, and we should parse it according to the method used to
* make that consensus.
*
* DOCDOC flav
**/
static routerstatus_t *
routerstatus_parse_entry_from_string(memarea_t *area,
const char **s, smartlist_t *tokens,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
int consensus_method)
int consensus_method,
consensus_flavor_t flav)
{
const char *eos, *s_dup = *s;
routerstatus_t *rs = NULL;
directory_token_t *tok;
char timebuf[ISO_TIME_LEN+1];
struct in_addr in;
int offset = 0;
tor_assert(tokens);
tor_assert(bool_eq(vote, vote_rs));
if (!consensus_method)
flav = FLAV_NS;
eos = find_start_of_next_routerstatus(*s);
if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
@ -1927,7 +1934,15 @@ routerstatus_parse_entry_from_string(memarea_t *area,
goto err;
}
tok = find_by_keyword(tokens, K_R);
tor_assert(tok->n_args >= 8);
tor_assert(tok->n_args >= 7);
if (flav == FLAV_NS) {
if (tok->n_args < 8) {
log_warn(LD_DIR, "Too few arguments to r");
goto err;
}
} else {
offset = -1;
}
if (vote_rs) {
rs = &vote_rs->status;
} else {
@ -1948,29 +1963,34 @@ routerstatus_parse_entry_from_string(memarea_t *area,
goto err;
}
if (flav == FLAV_NS) {
if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
log_warn(LD_DIR, "Error decoding descriptor digest %s",
escaped(tok->args[2]));
goto err;
}
}
if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
tok->args[3], tok->args[4]) < 0 ||
tok->args[3+offset], tok->args[4+offset]) < 0 ||
parse_iso_time(timebuf, &rs->published_on)<0) {
log_warn(LD_DIR, "Error parsing time '%s %s'",
tok->args[3], tok->args[4]);
log_warn(LD_DIR, "Error parsing time '%s %s' [%d %d]",
tok->args[3+offset], tok->args[4+offset],
offset, (int)flav);
goto err;
}
if (tor_inet_aton(tok->args[5], &in) == 0) {
if (tor_inet_aton(tok->args[5+offset], &in) == 0) {
log_warn(LD_DIR, "Error parsing router address in network-status %s",
escaped(tok->args[5]));
escaped(tok->args[5+offset]));
goto err;
}
rs->addr = ntohl(in.s_addr);
rs->or_port =(uint16_t) tor_parse_long(tok->args[6],10,0,65535,NULL,NULL);
rs->dir_port = (uint16_t) tor_parse_long(tok->args[7],10,0,65535,NULL,NULL);
rs->or_port = (uint16_t) tor_parse_long(tok->args[6+offset],
10,0,65535,NULL,NULL);
rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset],
10,0,65535,NULL,NULL);
tok = find_opt_by_keyword(tokens, K_S);
if (tok && vote) {
@ -2267,7 +2287,7 @@ networkstatus_v2_parse_from_string(const char *s)
while (!strcmpstart(s, "r ")) {
routerstatus_t *rs;
if ((rs = routerstatus_parse_entry_from_string(area, &s, tokens,
NULL, NULL, 0)))
NULL, NULL, 0, 0)))
smartlist_add(ns->entries, rs);
}
smartlist_sort(ns->entries, compare_routerstatus_entries);
@ -2329,6 +2349,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
struct in_addr in;
int i, inorder, n_signatures = 0;
memarea_t *area = NULL, *rs_area = NULL;
consensus_flavor_t flav = FLAV_NS;
tor_assert(s);
if (eos_out)
@ -2352,6 +2374,22 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
ns = tor_malloc_zero(sizeof(networkstatus_t));
memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
tok = find_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
tor_assert(tok);
if (tok->n_args > 1) {
int flavor = networkstatus_parse_flavor_name(tok->args[1]);
if (flavor < 0) {
log_warn(LD_DIR, "Can't parse document with unknown flavor %s",
escaped(tok->args[2]));
goto err;
}
ns->flavor = flav = flavor;
}
if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) {
log_warn(LD_DIR, "Flavor found on non-consenus networkstatus.");
goto err;
}
if (ns_type != NS_TYPE_CONSENSUS) {
const char *end_of_cert = NULL;
if (!(cert = strstr(s, "\ndir-key-certificate-version")))
@ -2598,7 +2636,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
if (ns->type != NS_TYPE_CONSENSUS) {
vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
if (routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, ns,
rs, 0))
rs, 0, 0))
smartlist_add(ns->routerstatus_list, rs);
else {
tor_free(rs->version);
@ -2608,7 +2646,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
routerstatus_t *rs;
if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens,
NULL, NULL,
ns->consensus_method)))
ns->consensus_method,
flav)))
smartlist_add(ns->routerstatus_list, rs);
}
}
@ -2645,10 +2684,29 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
char declared_identity[DIGEST_LEN];
networkstatus_voter_info_t *v;
document_signature_t *sig;
const char *id_hexdigest = NULL;
const char *sk_hexdigest = NULL;
digest_algorithm_t alg = DIGEST_SHA1;
tok = _tok;
if (tok->tp != K_DIRECTORY_SIGNATURE)
continue;
tor_assert(tok->n_args >= 2);
if (tok->n_args == 2) {
id_hexdigest = tok->args[0];
sk_hexdigest = tok->args[1];
} else {
const char *algname = tok->args[0];
int a;
id_hexdigest = tok->args[1];
sk_hexdigest = tok->args[2];
a = crypto_digest_algorithm_parse_name(algname);
if (a<0) {
log_warn(LD_DIR, "Unknown digest algorithm %s; skipping",
escaped(algname));
continue;
}
alg = a;
}
if (!tok->object_type ||
strcmp(tok->object_type, "SIGNATURE") ||
@ -2657,11 +2715,11 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
goto err;
}
if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
base16_decode(declared_identity, sizeof(declared_identity),
tok->args[0], HEX_DIGEST_LEN) < 0) {
id_hexdigest, HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared identity %s in "
"network-status vote.", escaped(tok->args[0]));
"network-status vote.", escaped(id_hexdigest));
goto err;
}
if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
@ -2671,12 +2729,12 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
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 ||
sig->alg = alg;
if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
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]));
sk_hexdigest, HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
"network-status vote.", escaped(sk_hexdigest));
tor_free(sig);
goto err;
}
@ -2716,7 +2774,6 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
sig->signature = tor_memdup(tok->object_body, tok->object_size);
sig->signature_len = (int) tok->object_size;
}
smartlist_add(v->sigs, sig);
@ -2950,10 +3007,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
"network-status vote.", escaped(id_hexdigest));
goto err;
}
if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
base16_decode(sk_digest, sizeof(sk_digest),
sk_hexdigest, HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared digest %s in "
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
"network-status vote.", escaped(sk_hexdigest));
goto err;
}
@ -2974,7 +3031,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
}
sig = tor_malloc_zero(sizeof(document_signature_t));
sig->alg = DIGEST_SHA1;
sig->alg = alg;
memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
if (tok->object_size >= INT_MAX) {

View File

@ -559,16 +559,18 @@ generate_ri_from_rs(const vote_routerstatus_t *vrs)
return r;
}
/** Helper: get a detached signatures document for a single FLAV_NS
* consensus. */
/** Helper: get a detached signatures document for one or two
* consensuses. */
static char *
get_detached_sigs(networkstatus_t *ns)
get_detached_sigs(networkstatus_t *ns, networkstatus_t *ns2)
{
char *r;
smartlist_t *sl;
tor_assert(ns && ns->flavor == FLAV_NS);
sl = smartlist_create();
smartlist_add(sl,ns);
if (ns2)
smartlist_add(sl,ns2);
r = networkstatus_get_detached_signatures(sl);
smartlist_free(sl);
return r;
@ -587,7 +589,8 @@ 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;
networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL,
*con_md=NULL;
vote_routerstatus_t *vrs;
routerstatus_t *rs;
char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL, *cp;
@ -596,7 +599,9 @@ test_dir_v3_networkstatus(void)
/* For generating the two other consensuses. */
char *detached_text1=NULL, *detached_text2=NULL;
char *consensus_text2=NULL, *consensus_text3=NULL;
networkstatus_t *con2=NULL, *con3=NULL;
char *consensus_text_md2=NULL, *consensus_text_md3=NULL;
char *consensus_text_md=NULL;
networkstatus_t *con2=NULL, *con_md2=NULL, *con3=NULL, *con_md3=NULL;
ns_detached_signatures_t *dsig1=NULL, *dsig2=NULL;
/* Parse certificates and keys. */
@ -882,6 +887,17 @@ test_dir_v3_networkstatus(void)
test_assert(con);
//log_notice(LD_GENERAL, "<<%s>>\n<<%s>>\n<<%s>>\n",
// v1_text, v2_text, v3_text);
consensus_text_md = networkstatus_compute_consensus(votes, 3,
cert3->identity_key,
sign_skey_3,
"AAAAAAAAAAAAAAAAAAAA",
sign_skey_leg1,
FLAV_MICRODESC);
test_assert(consensus_text_md);
con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
NS_TYPE_CONSENSUS);
test_assert(con_md);
test_eq(con_md->flavor, FLAV_MICRODESC);
/* Check consensus contents. */
test_assert(con->type == NS_TYPE_CONSENSUS);
@ -956,19 +972,11 @@ test_dir_v3_networkstatus(void)
test_assert(rs->is_valid);
test_assert(!rs->is_named);
/* XXXX check version */
// x231
// x213
/* 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_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_eq(smartlist_len(voter->sigs), 1);
@ -990,26 +998,45 @@ test_dir_v3_networkstatus(void)
cert2->identity_key,
sign_skey_2, NULL,NULL,
FLAV_NS);
consensus_text_md2 = networkstatus_compute_consensus(votes, 3,
cert2->identity_key,
sign_skey_2, NULL,NULL,
FLAV_MICRODESC);
smartlist_shuffle(votes);
consensus_text3 = networkstatus_compute_consensus(votes, 3,
cert1->identity_key,
sign_skey_1, NULL,NULL,
FLAV_NS);
consensus_text_md3 = networkstatus_compute_consensus(votes, 3,
cert1->identity_key,
sign_skey_1, NULL,NULL,
FLAV_MICRODESC);
test_assert(consensus_text2);
test_assert(consensus_text3);
test_assert(consensus_text_md2);
test_assert(consensus_text_md3);
con2 = networkstatus_parse_vote_from_string(consensus_text2, NULL,
NS_TYPE_CONSENSUS);
con3 = networkstatus_parse_vote_from_string(consensus_text3, NULL,
NS_TYPE_CONSENSUS);
con_md2 = networkstatus_parse_vote_from_string(consensus_text_md2, NULL,
NS_TYPE_CONSENSUS);
con_md3 = networkstatus_parse_vote_from_string(consensus_text_md3, NULL,
NS_TYPE_CONSENSUS);
test_assert(con2);
test_assert(con3);
test_assert(con_md2);
test_assert(con_md3);
/* All three should have the same digest. */
test_memeq(&con->digests, &con2->digests, sizeof(digests_t));
test_memeq(&con->digests, &con3->digests, sizeof(digests_t));
test_memeq(&con_md->digests, &con_md2->digests, sizeof(digests_t));
test_memeq(&con_md->digests, &con_md3->digests, sizeof(digests_t));
/* Extract a detached signature from con3. */
detached_text1 = get_detached_sigs(con3);
detached_text1 = get_detached_sigs(con3, con_md3);
tor_assert(detached_text1);
/* Try to parse it. */
dsig1 = networkstatus_parse_detached_signatures(detached_text1, NULL);
@ -1024,6 +1051,11 @@ test_dir_v3_networkstatus(void)
test_assert(dsig_digests);
test_memeq(dsig_digests->d[DIGEST_SHA1], con3->digests.d[DIGEST_SHA1],
DIGEST_LEN);
dsig_digests = strmap_get(dsig1->digests, "microdesc");
test_assert(dsig_digests);
test_memeq(dsig_digests->d[DIGEST_SHA256],
con_md3->digests.d[DIGEST_SHA256],
DIGEST256_LEN);
}
{
smartlist_t *dsig_signatures = strmap_get(dsig1->signatures, "ns");
@ -1032,13 +1064,24 @@ test_dir_v3_networkstatus(void)
sig = smartlist_get(dsig_signatures, 0);
test_memeq(sig->identity_digest, cert1->cache_info.identity_digest,
DIGEST_LEN);
test_eq(sig->alg, DIGEST_SHA1);
dsig_signatures = strmap_get(dsig1->signatures, "microdesc");
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);
test_eq(sig->alg, DIGEST_SHA256);
}
/* Try adding it to con2. */
detached_text2 = get_detached_sigs(con2);
detached_text2 = get_detached_sigs(con2,con_md2);
test_eq(1, networkstatus_add_detached_signatures(con2, dsig1, &msg));
tor_free(detached_text2);
detached_text2 = get_detached_sigs(con2);
test_eq(1, networkstatus_add_detached_signatures(con_md2, dsig1, &msg));
tor_free(detached_text2);
detached_text2 = get_detached_sigs(con2,con_md2);
//printf("\n<%s>\n", detached_text2);
dsig2 = networkstatus_parse_detached_signatures(detached_text2, NULL);
test_assert(dsig2);
@ -1052,6 +1095,9 @@ test_dir_v3_networkstatus(void)
*/
test_eq(2,
smartlist_len((smartlist_t*)strmap_get(dsig2->signatures, "ns")));
test_eq(2,
smartlist_len((smartlist_t*)strmap_get(dsig2->signatures,
"microdesc")));
/* Try adding to con2 twice; verify that nothing changes. */
test_eq(0, networkstatus_add_detached_signatures(con2, dsig1, &msg));
@ -1075,6 +1121,7 @@ test_dir_v3_networkstatus(void)
tor_free(v2_text);
tor_free(v3_text);
tor_free(consensus_text);
tor_free(consensus_text_md);
if (vote)
networkstatus_vote_free(vote);
@ -1086,6 +1133,8 @@ test_dir_v3_networkstatus(void)
networkstatus_vote_free(v3);
if (con)
networkstatus_vote_free(con);
if (con_md)
networkstatus_vote_free(con_md);
if (sign_skey_1)
crypto_free_pk_env(sign_skey_1);
if (sign_skey_2)
@ -1103,12 +1152,18 @@ test_dir_v3_networkstatus(void)
tor_free(consensus_text2);
tor_free(consensus_text3);
tor_free(consensus_text_md2);
tor_free(consensus_text_md3);
tor_free(detached_text1);
tor_free(detached_text2);
if (con2)
networkstatus_vote_free(con2);
if (con3)
networkstatus_vote_free(con3);
if (con_md2)
networkstatus_vote_free(con_md2);
if (con_md3)
networkstatus_vote_free(con_md3);
if (dsig1)
ns_detached_signatures_free(dsig1);
if (dsig2)