Merge branch 'maint-0.4.8' into release-0.4.8
[tor.git] / src / feature / dirauth / dsigs_parse.c
blob5ac2ff6e49086972663ee2f052171986ede31469
1 /* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
7 /**
8 * \file dsigs_parse.h
9 * \brief Code to parse and validate detached-signature objects
10 **/
12 #include "core/or/or.h"
13 #include "feature/dirparse/parsecommon.h"
14 #include "feature/dirparse/unparseable.h"
15 #include "feature/nodelist/networkstatus.h"
16 #include "lib/memarea/memarea.h"
18 #include "feature/dirauth/dsigs_parse.h"
19 #include "feature/dirauth/ns_detached_signatures_st.h"
20 #include "feature/nodelist/document_signature_st.h"
22 /** List of tokens recognized in detached networkstatus signature documents. */
23 static token_rule_t networkstatus_detached_signature_token_table[] = {
24 T1_START("consensus-digest", K_CONSENSUS_DIGEST, GE(1), NO_OBJ ),
25 T("additional-digest", K_ADDITIONAL_DIGEST,GE(3), NO_OBJ ),
26 T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
27 T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
28 T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
29 T("additional-signature", K_ADDITIONAL_SIGNATURE, GE(4), NEED_OBJ ),
30 T1N("directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
31 END_OF_TABLE
34 /** Return the common_digests_t that holds the digests of the
35 * <b>flavor_name</b>-flavored networkstatus according to the detached
36 * signatures document <b>sigs</b>, allocating a new common_digests_t as
37 * needed. */
38 static common_digests_t *
39 detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
41 common_digests_t *d = strmap_get(sigs->digests, flavor_name);
42 if (!d) {
43 d = tor_malloc_zero(sizeof(common_digests_t));
44 strmap_set(sigs->digests, flavor_name, d);
46 return d;
49 /** Return the list of signatures of the <b>flavor_name</b>-flavored
50 * networkstatus according to the detached signatures document <b>sigs</b>,
51 * allocating a new common_digests_t as needed. */
52 static smartlist_t *
53 detached_get_signatures(ns_detached_signatures_t *sigs,
54 const char *flavor_name)
56 smartlist_t *sl = strmap_get(sigs->signatures, flavor_name);
57 if (!sl) {
58 sl = smartlist_new();
59 strmap_set(sigs->signatures, flavor_name, sl);
61 return sl;
64 /** Parse a detached v3 networkstatus signature document between <b>s</b> and
65 * <b>eos</b> and return the result. Return -1 on failure. */
66 ns_detached_signatures_t *
67 networkstatus_parse_detached_signatures(const char *s, const char *eos)
69 /* XXXX there is too much duplicate shared between this function and
70 * networkstatus_parse_vote_from_string(). */
71 directory_token_t *tok;
72 memarea_t *area = NULL;
73 common_digests_t *digests;
75 smartlist_t *tokens = smartlist_new();
76 ns_detached_signatures_t *sigs =
77 tor_malloc_zero(sizeof(ns_detached_signatures_t));
78 sigs->digests = strmap_new();
79 sigs->signatures = strmap_new();
81 if (!eos)
82 eos = s + strlen(s);
84 area = memarea_new();
85 if (tokenize_string(area,s, eos, tokens,
86 networkstatus_detached_signature_token_table, 0)) {
87 log_warn(LD_DIR, "Error tokenizing detached networkstatus signatures");
88 goto err;
91 /* Grab all the digest-like tokens. */
92 SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
93 const char *algname;
94 digest_algorithm_t alg;
95 const char *flavor;
96 const char *hexdigest;
97 size_t expected_length, digest_length;
99 tok = _tok;
101 if (tok->tp == K_CONSENSUS_DIGEST) {
102 algname = "sha1";
103 alg = DIGEST_SHA1;
104 flavor = "ns";
105 hexdigest = tok->args[0];
106 } else if (tok->tp == K_ADDITIONAL_DIGEST) {
107 int a = crypto_digest_algorithm_parse_name(tok->args[1]);
108 if (a<0) {
109 log_warn(LD_DIR, "Unrecognized algorithm name %s", tok->args[0]);
110 continue;
112 alg = (digest_algorithm_t) a;
113 flavor = tok->args[0];
114 algname = tok->args[1];
115 hexdigest = tok->args[2];
116 } else {
117 continue;
120 digest_length = crypto_digest_algorithm_get_length(alg);
121 expected_length = digest_length * 2; /* hex encoding */
123 if (strlen(hexdigest) != expected_length) {
124 log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
125 "networkstatus signatures");
126 goto err;
128 digests = detached_get_digests(sigs, flavor);
129 tor_assert(digests);
130 if (!fast_mem_is_zero(digests->d[alg], digest_length)) {
131 log_warn(LD_DIR, "Multiple digests for %s with %s on detached "
132 "signatures document", flavor, algname);
133 continue;
135 if (base16_decode(digests->d[alg], digest_length,
136 hexdigest, strlen(hexdigest)) != (int) digest_length) {
137 log_warn(LD_DIR, "Bad encoding on consensus-digest in detached "
138 "networkstatus signatures");
139 goto err;
141 } SMARTLIST_FOREACH_END(_tok);
143 tok = find_by_keyword(tokens, K_VALID_AFTER);
144 if (parse_iso_time(tok->args[0], &sigs->valid_after)) {
145 log_warn(LD_DIR, "Bad valid-after in detached networkstatus signatures");
146 goto err;
149 tok = find_by_keyword(tokens, K_FRESH_UNTIL);
150 if (parse_iso_time(tok->args[0], &sigs->fresh_until)) {
151 log_warn(LD_DIR, "Bad fresh-until in detached networkstatus signatures");
152 goto err;
155 tok = find_by_keyword(tokens, K_VALID_UNTIL);
156 if (parse_iso_time(tok->args[0], &sigs->valid_until)) {
157 log_warn(LD_DIR, "Bad valid-until in detached networkstatus signatures");
158 goto err;
161 SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
162 const char *id_hexdigest;
163 const char *sk_hexdigest;
164 const char *algname;
165 const char *flavor;
166 digest_algorithm_t alg;
168 char id_digest[DIGEST_LEN];
169 char sk_digest[DIGEST_LEN];
170 smartlist_t *siglist;
171 document_signature_t *sig;
172 int is_duplicate;
174 tok = _tok;
175 if (tok->tp == K_DIRECTORY_SIGNATURE) {
176 tor_assert(tok->n_args >= 2);
177 flavor = "ns";
178 algname = "sha1";
179 id_hexdigest = tok->args[0];
180 sk_hexdigest = tok->args[1];
181 } else if (tok->tp == K_ADDITIONAL_SIGNATURE) {
182 tor_assert(tok->n_args >= 4);
183 flavor = tok->args[0];
184 algname = tok->args[1];
185 id_hexdigest = tok->args[2];
186 sk_hexdigest = tok->args[3];
187 } else {
188 continue;
192 int a = crypto_digest_algorithm_parse_name(algname);
193 if (a<0) {
194 log_warn(LD_DIR, "Unrecognized algorithm name %s", algname);
195 continue;
197 alg = (digest_algorithm_t) a;
200 if (!tok->object_type ||
201 strcmp(tok->object_type, "SIGNATURE") ||
202 tok->object_size < 128 || tok->object_size > 512) {
203 log_warn(LD_DIR, "Bad object type or length on directory-signature");
204 goto err;
207 if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
208 base16_decode(id_digest, sizeof(id_digest),
209 id_hexdigest, HEX_DIGEST_LEN) != sizeof(id_digest)) {
210 log_warn(LD_DIR, "Error decoding declared identity %s in "
211 "network-status vote.", escaped(id_hexdigest));
212 goto err;
214 if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
215 base16_decode(sk_digest, sizeof(sk_digest),
216 sk_hexdigest, HEX_DIGEST_LEN) != sizeof(sk_digest)) {
217 log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
218 "network-status vote.", escaped(sk_hexdigest));
219 goto err;
222 siglist = detached_get_signatures(sigs, flavor);
223 is_duplicate = 0;
224 SMARTLIST_FOREACH(siglist, document_signature_t *, dsig, {
225 if (dsig->alg == alg &&
226 tor_memeq(id_digest, dsig->identity_digest, DIGEST_LEN) &&
227 tor_memeq(sk_digest, dsig->signing_key_digest, DIGEST_LEN)) {
228 is_duplicate = 1;
231 if (is_duplicate) {
232 log_warn(LD_DIR, "Two signatures with identical keys and algorithm "
233 "found.");
234 continue;
237 sig = tor_malloc_zero(sizeof(document_signature_t));
238 sig->alg = alg;
239 memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
240 memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
241 if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
242 tor_free(sig);
243 goto err;
245 sig->signature = tor_memdup(tok->object_body, tok->object_size);
246 sig->signature_len = (int) tok->object_size;
248 smartlist_add(siglist, sig);
249 } SMARTLIST_FOREACH_END(_tok);
251 goto done;
252 err:
253 ns_detached_signatures_free(sigs);
254 sigs = NULL;
255 done:
256 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
257 smartlist_free(tokens);
258 if (area) {
259 DUMP_AREA(area, "detached signatures");
260 memarea_drop_all(area);
262 return sigs;
265 /** Release all storage held in <b>s</b>. */
266 void
267 ns_detached_signatures_free_(ns_detached_signatures_t *s)
269 if (!s)
270 return;
271 if (s->signatures) {
272 STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) {
273 SMARTLIST_FOREACH(sigs, document_signature_t *, sig,
274 document_signature_free(sig));
275 smartlist_free(sigs);
276 } STRMAP_FOREACH_END;
277 strmap_free(s->signatures, NULL);
278 strmap_free(s->digests, tor_free_);
281 tor_free(s);