]>
Commit | Line | Data |
---|---|---|
2b442ac8 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2015 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <gcrypt.h> | |
23 | ||
24 | #include "alloc-util.h" | |
25 | #include "dns-domain.h" | |
26 | #include "resolved-dns-dnssec.h" | |
27 | #include "resolved-dns-packet.h" | |
28 | ||
29 | /* Open question: | |
30 | * | |
31 | * How does the DNSSEC canonical form of a hostname with a label | |
32 | * containing a dot look like, the way DNS-SD does it? | |
33 | * | |
34 | * */ | |
35 | ||
36 | #define VERIFY_RRS_MAX 256 | |
37 | #define MAX_KEY_SIZE (32*1024) | |
38 | ||
39 | /* | |
40 | * The DNSSEC Chain of trust: | |
41 | * | |
42 | * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone | |
43 | * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree | |
44 | * DS RRs are protected like normal RRs | |
45 | * | |
46 | * Example chain: | |
47 | * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS | |
48 | */ | |
49 | ||
50 | static bool dnssec_algorithm_supported(int algorithm) { | |
51 | return IN_SET(algorithm, DNSSEC_ALGORITHM_RSASHA1, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_ALGORITHM_RSASHA512); | |
52 | } | |
53 | ||
54 | static bool dnssec_digest_supported(int digest) { | |
55 | return IN_SET(digest, DNSSEC_DIGEST_SHA1, DNSSEC_DIGEST_SHA256); | |
56 | } | |
57 | ||
58 | uint16_t dnssec_keytag(DnsResourceRecord *dnskey) { | |
59 | const uint8_t *p; | |
60 | uint32_t sum; | |
61 | size_t i; | |
62 | ||
63 | /* The algorithm from RFC 4034, Appendix B. */ | |
64 | ||
65 | assert(dnskey); | |
66 | assert(dnskey->key->type == DNS_TYPE_DNSKEY); | |
67 | ||
68 | sum = (uint32_t) dnskey->dnskey.flags + | |
69 | ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm); | |
70 | ||
71 | p = dnskey->dnskey.key; | |
72 | ||
73 | for (i = 0; i < dnskey->dnskey.key_size; i++) | |
74 | sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i]; | |
75 | ||
76 | sum += (sum >> 16) & UINT32_C(0xFFFF); | |
77 | ||
78 | return sum & UINT32_C(0xFFFF); | |
79 | } | |
80 | ||
81 | static int rr_compare(const void *a, const void *b) { | |
82 | DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b; | |
83 | size_t m; | |
84 | int r; | |
85 | ||
86 | /* Let's order the RRs according to RFC 4034, Section 6.3 */ | |
87 | ||
88 | assert(x); | |
89 | assert(*x); | |
90 | assert((*x)->wire_format); | |
91 | assert(y); | |
92 | assert(*y); | |
93 | assert((*y)->wire_format); | |
94 | ||
95 | m = MIN((*x)->wire_format_size, (*y)->wire_format_size); | |
96 | ||
97 | r = memcmp((*x)->wire_format, (*y)->wire_format, m); | |
98 | if (r != 0) | |
99 | return r; | |
100 | ||
101 | if ((*x)->wire_format_size < (*y)->wire_format_size) | |
102 | return -1; | |
103 | else if ((*x)->wire_format_size > (*y)->wire_format_size) | |
104 | return 1; | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | static int dnssec_rsa_verify( | |
110 | const char *hash_algorithm, | |
111 | const void *signature, size_t signature_size, | |
112 | const void *data, size_t data_size, | |
113 | const void *exponent, size_t exponent_size, | |
114 | const void *modulus, size_t modulus_size) { | |
115 | ||
116 | gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL; | |
117 | gcry_mpi_t n = NULL, e = NULL, s = NULL; | |
118 | gcry_error_t ge; | |
119 | int r; | |
120 | ||
121 | assert(hash_algorithm); | |
122 | ||
123 | ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL); | |
124 | if (ge != 0) { | |
125 | r = -EIO; | |
126 | goto finish; | |
127 | } | |
128 | ||
129 | ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL); | |
130 | if (ge != 0) { | |
131 | r = -EIO; | |
132 | goto finish; | |
133 | } | |
134 | ||
135 | ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL); | |
136 | if (ge != 0) { | |
137 | r = -EIO; | |
138 | goto finish; | |
139 | } | |
140 | ||
141 | ge = gcry_sexp_build(&signature_sexp, | |
142 | NULL, | |
143 | "(sig-val (rsa (s %m)))", | |
144 | s); | |
145 | ||
146 | if (ge != 0) { | |
147 | r = -EIO; | |
148 | goto finish; | |
149 | } | |
150 | ||
151 | ge = gcry_sexp_build(&data_sexp, | |
152 | NULL, | |
153 | "(data (flags pkcs1) (hash %s %b))", | |
154 | hash_algorithm, | |
155 | (int) data_size, | |
156 | data); | |
157 | if (ge != 0) { | |
158 | r = -EIO; | |
159 | goto finish; | |
160 | } | |
161 | ||
162 | ge = gcry_sexp_build(&public_key_sexp, | |
163 | NULL, | |
164 | "(public-key (rsa (n %m) (e %m)))", | |
165 | n, | |
166 | e); | |
167 | if (ge != 0) { | |
168 | r = -EIO; | |
169 | goto finish; | |
170 | } | |
171 | ||
172 | ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp); | |
173 | if (ge == GPG_ERR_BAD_SIGNATURE) | |
174 | r = 0; | |
175 | else if (ge != 0) | |
176 | r = -EIO; | |
177 | else | |
178 | r = 1; | |
179 | ||
180 | finish: | |
181 | if (e) | |
182 | gcry_mpi_release(e); | |
183 | if (n) | |
184 | gcry_mpi_release(n); | |
185 | if (s) | |
186 | gcry_mpi_release(s); | |
187 | ||
188 | if (public_key_sexp) | |
189 | gcry_sexp_release(public_key_sexp); | |
190 | if (signature_sexp) | |
191 | gcry_sexp_release(signature_sexp); | |
192 | if (data_sexp) | |
193 | gcry_sexp_release(data_sexp); | |
194 | ||
195 | return r; | |
196 | } | |
197 | ||
198 | static void md_add_uint8(gcry_md_hd_t md, uint8_t v) { | |
199 | gcry_md_write(md, &v, sizeof(v)); | |
200 | } | |
201 | ||
202 | static void md_add_uint16(gcry_md_hd_t md, uint16_t v) { | |
203 | v = htobe16(v); | |
204 | gcry_md_write(md, &v, sizeof(v)); | |
205 | } | |
206 | ||
207 | static void md_add_uint32(gcry_md_hd_t md, uint32_t v) { | |
208 | v = htobe32(v); | |
209 | gcry_md_write(md, &v, sizeof(v)); | |
210 | } | |
211 | ||
212 | int dnssec_verify_rrset(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { | |
213 | uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX]; | |
214 | size_t exponent_size, modulus_size, hash_size; | |
215 | void *exponent, *modulus, *hash; | |
216 | DnsResourceRecord **list, *rr; | |
217 | gcry_md_hd_t md = NULL; | |
218 | size_t k, n = 0; | |
219 | int r; | |
220 | ||
221 | assert(key); | |
222 | assert(rrsig); | |
223 | assert(dnskey); | |
224 | ||
225 | /* Verifies the the RRSet matching the specified "key" in "a", | |
226 | * using the signature "rrsig" and the key "dnskey". It's | |
227 | * assumed the RRSIG and DNSKEY match. */ | |
228 | ||
229 | if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) | |
230 | return -EOPNOTSUPP; | |
231 | ||
232 | if (a->n_rrs > VERIFY_RRS_MAX) | |
233 | return -E2BIG; | |
234 | ||
235 | /* Collect all relevant RRs in a single array, so that we can look at the RRset */ | |
236 | list = newa(DnsResourceRecord *, a->n_rrs); | |
237 | ||
238 | DNS_ANSWER_FOREACH(rr, a) { | |
239 | r = dns_resource_key_equal(key, rr->key); | |
240 | if (r < 0) | |
241 | return r; | |
242 | if (r == 0) | |
243 | continue; | |
244 | ||
245 | /* We need the wire format for ordering, and digest calculation */ | |
246 | r = dns_resource_record_to_wire_format(rr, true); | |
247 | if (r < 0) | |
248 | return r; | |
249 | ||
250 | list[n++] = rr; | |
251 | } | |
252 | ||
253 | if (n <= 0) | |
254 | return -ENODATA; | |
255 | ||
256 | /* Bring the RRs into canonical order */ | |
257 | qsort_safe(list, n, sizeof(DnsResourceRecord), rr_compare); | |
258 | ||
259 | /* OK, the RRs are now in canonical order. Let's calculate the digest */ | |
260 | switch (rrsig->rrsig.algorithm) { | |
261 | ||
262 | case DNSSEC_ALGORITHM_RSASHA1: | |
263 | gcry_md_open(&md, GCRY_MD_SHA1, 0); | |
264 | hash_size = 20; | |
265 | break; | |
266 | ||
267 | case DNSSEC_ALGORITHM_RSASHA256: | |
268 | gcry_md_open(&md, GCRY_MD_SHA256, 0); | |
269 | hash_size = 32; | |
270 | break; | |
271 | ||
272 | case DNSSEC_ALGORITHM_RSASHA512: | |
273 | gcry_md_open(&md, GCRY_MD_SHA512, 0); | |
274 | hash_size = 64; | |
275 | break; | |
276 | ||
277 | default: | |
278 | assert_not_reached("Unknown digest"); | |
279 | } | |
280 | ||
281 | if (!md) | |
282 | return -EIO; | |
283 | ||
284 | md_add_uint16(md, rrsig->rrsig.type_covered); | |
285 | md_add_uint8(md, rrsig->rrsig.algorithm); | |
286 | md_add_uint8(md, rrsig->rrsig.labels); | |
287 | md_add_uint32(md, rrsig->rrsig.original_ttl); | |
288 | md_add_uint32(md, rrsig->rrsig.expiration); | |
289 | md_add_uint32(md, rrsig->rrsig.inception); | |
290 | md_add_uint16(md, rrsig->rrsig.key_tag); | |
291 | ||
292 | r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true); | |
293 | if (r < 0) | |
294 | goto finish; | |
295 | gcry_md_write(md, wire_format_name, r); | |
296 | ||
297 | for (k = 0; k < n; k++) { | |
298 | size_t l; | |
299 | rr = list[k]; | |
300 | ||
301 | r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true); | |
302 | if (r < 0) | |
303 | goto finish; | |
304 | gcry_md_write(md, wire_format_name, r); | |
305 | ||
306 | md_add_uint16(md, rr->key->type); | |
307 | md_add_uint16(md, rr->key->class); | |
308 | md_add_uint32(md, rrsig->rrsig.original_ttl); | |
309 | ||
310 | assert(rr->wire_format_rdata_offset <= rr->wire_format_size); | |
311 | l = rr->wire_format_size - rr->wire_format_rdata_offset; | |
312 | assert(l <= 0xFFFF); | |
313 | ||
314 | md_add_uint16(md, (uint16_t) l); | |
315 | gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l); | |
316 | } | |
317 | ||
318 | hash = gcry_md_read(md, 0); | |
319 | if (!hash) { | |
320 | r = -EIO; | |
321 | goto finish; | |
322 | } | |
323 | ||
324 | if (*(uint8_t*) dnskey->dnskey.key == 0) { | |
325 | /* exponent is > 255 bytes long */ | |
326 | ||
327 | exponent = (uint8_t*) dnskey->dnskey.key + 3; | |
328 | exponent_size = | |
329 | ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) | | |
330 | ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]); | |
331 | ||
332 | if (exponent_size < 256) { | |
333 | r = -EINVAL; | |
334 | goto finish; | |
335 | } | |
336 | ||
337 | if (3 + exponent_size >= dnskey->dnskey.key_size) { | |
338 | r = -EINVAL; | |
339 | goto finish; | |
340 | } | |
341 | ||
342 | modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size; | |
343 | modulus_size = dnskey->dnskey.key_size - 3 - exponent_size; | |
344 | ||
345 | } else { | |
346 | /* exponent is <= 255 bytes long */ | |
347 | ||
348 | exponent = (uint8_t*) dnskey->dnskey.key + 1; | |
349 | exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0]; | |
350 | ||
351 | if (exponent_size <= 0) { | |
352 | r = -EINVAL; | |
353 | goto finish; | |
354 | } | |
355 | ||
356 | if (1 + exponent_size >= dnskey->dnskey.key_size) { | |
357 | r = -EINVAL; | |
358 | goto finish; | |
359 | } | |
360 | ||
361 | modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size; | |
362 | modulus_size = dnskey->dnskey.key_size - 1 - exponent_size; | |
363 | } | |
364 | ||
365 | r = dnssec_rsa_verify( | |
366 | gcry_md_algo_name(gcry_md_get_algo(md)), | |
367 | rrsig->rrsig.signature, rrsig->rrsig.signature_size, | |
368 | hash, hash_size, | |
369 | exponent, exponent_size, | |
370 | modulus, modulus_size); | |
371 | if (r < 0) | |
372 | goto finish; | |
373 | ||
374 | r = r ? DNSSEC_VERIFIED : DNSSEC_INVALID; | |
375 | ||
376 | finish: | |
377 | gcry_md_close(md); | |
378 | return r; | |
379 | } | |
380 | ||
381 | int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { | |
382 | ||
383 | assert(rrsig); | |
384 | assert(dnskey); | |
385 | ||
386 | /* Checks if the specified DNSKEY RR matches the key used for | |
387 | * the signature in the specified RRSIG RR */ | |
388 | ||
389 | if (rrsig->key->type != DNS_TYPE_RRSIG) | |
390 | return -EINVAL; | |
391 | ||
392 | if (dnskey->key->type != DNS_TYPE_DNSKEY) | |
393 | return 0; | |
394 | if (dnskey->key->class != rrsig->key->class) | |
395 | return 0; | |
396 | if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) | |
397 | return 0; | |
398 | if (dnskey->dnskey.protocol != 3) | |
399 | return 0; | |
400 | if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm) | |
401 | return 0; | |
402 | ||
403 | if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag) | |
404 | return 0; | |
405 | ||
406 | return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(rrsig->key)); | |
407 | } | |
408 | ||
409 | int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) { | |
410 | assert(key); | |
411 | assert(rrsig); | |
412 | ||
413 | /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */ | |
414 | ||
415 | if (rrsig->key->type != DNS_TYPE_RRSIG) | |
416 | return 0; | |
417 | if (rrsig->key->class != key->class) | |
418 | return 0; | |
419 | if (rrsig->rrsig.type_covered != key->type) | |
420 | return 0; | |
421 | ||
422 | return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key)); | |
423 | } | |
424 | ||
425 | int dnssec_verify_rrset_search(DnsAnswer *a, DnsResourceKey *key, DnsAnswer *validated_dnskeys) { | |
426 | bool found_rrsig = false, found_dnskey = false; | |
427 | DnsResourceRecord *rrsig; | |
428 | int r; | |
429 | ||
430 | assert(key); | |
431 | ||
432 | /* Verifies all RRs from "a" that match the key "key", against DNSKEY RRs in "validated_dnskeys" */ | |
433 | ||
434 | if (!a || a->n_rrs <= 0) | |
435 | return -ENODATA; | |
436 | ||
437 | /* Iterate through each RRSIG RR. */ | |
438 | DNS_ANSWER_FOREACH(rrsig, a) { | |
439 | DnsResourceRecord *dnskey; | |
440 | ||
441 | r = dnssec_key_match_rrsig(key, rrsig); | |
442 | if (r < 0) | |
443 | return r; | |
444 | if (r == 0) | |
445 | continue; | |
446 | ||
447 | found_rrsig = true; | |
448 | ||
449 | DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) { | |
450 | ||
451 | r = dnssec_rrsig_match_dnskey(rrsig, dnskey); | |
452 | if (r < 0) | |
453 | return r; | |
454 | if (r == 0) | |
455 | continue; | |
456 | ||
457 | found_dnskey = true; | |
458 | ||
459 | /* Yay, we found a matching RRSIG with a matching | |
460 | * DNSKEY, awesome. Now let's verify all entries of | |
461 | * the RRSet against the RRSIG and DNSKEY | |
462 | * combination. */ | |
463 | ||
464 | r = dnssec_verify_rrset(a, key, rrsig, dnskey); | |
465 | if (r < 0 && r != EOPNOTSUPP) | |
466 | return r; | |
467 | if (r == DNSSEC_VERIFIED) | |
468 | return DNSSEC_VERIFIED; | |
469 | ||
470 | /* If the signature is invalid, or done using | |
471 | an unsupported algorithm, let's try another | |
472 | key and/or signature. After all they | |
473 | key_tags and stuff are not unique, and | |
474 | might be shared by multiple keys. */ | |
475 | } | |
476 | } | |
477 | ||
478 | if (found_dnskey) | |
479 | return DNSSEC_INVALID; | |
480 | ||
481 | if (found_rrsig) | |
482 | return DNSSEC_MISSING_KEY; | |
483 | ||
484 | return DNSSEC_NO_SIGNATURE; | |
485 | } | |
486 | ||
487 | int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { | |
488 | _cleanup_free_ char *s = NULL; | |
489 | size_t c = 0; | |
490 | int r; | |
491 | ||
492 | /* Converts the specified hostname into DNSSEC canonicalized | |
493 | * form. */ | |
494 | ||
495 | if (buffer_max < 2) | |
496 | return -ENOBUFS; | |
497 | ||
498 | for (;;) { | |
499 | size_t i; | |
500 | ||
501 | r = dns_label_unescape(&n, buffer, buffer_max); | |
502 | if (r < 0) | |
503 | return r; | |
504 | if (r == 0) | |
505 | break; | |
506 | if (r > 0) { | |
507 | int k; | |
508 | ||
509 | /* DNSSEC validation is always done on the ASCII version of the label */ | |
510 | k = dns_label_apply_idna(buffer, r, buffer, buffer_max); | |
511 | if (k < 0) | |
512 | return k; | |
513 | if (k > 0) | |
514 | r = k; | |
515 | } | |
516 | ||
517 | if (buffer_max < (size_t) r + 2) | |
518 | return -ENOBUFS; | |
519 | ||
520 | /* The DNSSEC canonical form is not clear on what to | |
521 | * do with dots appearing in labels, the way DNS-SD | |
522 | * does it. Refuse it for now. */ | |
523 | ||
524 | if (memchr(buffer, '.', r)) | |
525 | return -EINVAL; | |
526 | ||
527 | for (i = 0; i < (size_t) r; i ++) { | |
528 | if (buffer[i] >= 'A' && buffer[i] <= 'Z') | |
529 | buffer[i] = buffer[i] - 'A' + 'a'; | |
530 | } | |
531 | ||
532 | buffer[r] = '.'; | |
533 | ||
534 | buffer += r + 1; | |
535 | c += r + 1; | |
536 | ||
537 | buffer_max -= r + 1; | |
538 | } | |
539 | ||
540 | if (c <= 0) { | |
541 | /* Not even a single label: this is the root domain name */ | |
542 | ||
543 | assert(buffer_max > 2); | |
544 | buffer[0] = '.'; | |
545 | buffer[1] = 0; | |
546 | ||
547 | return 1; | |
548 | } | |
549 | ||
550 | return (int) c; | |
551 | } | |
552 | ||
553 | int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { | |
554 | gcry_md_hd_t md = NULL; | |
555 | char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX]; | |
556 | void *result; | |
557 | int r; | |
558 | ||
559 | assert(dnskey); | |
560 | assert(ds); | |
561 | ||
562 | /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */ | |
563 | ||
564 | if (dnskey->key->type != DNS_TYPE_DNSKEY) | |
565 | return -EINVAL; | |
566 | if (ds->key->type != DNS_TYPE_DS) | |
567 | return -EINVAL; | |
568 | if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) | |
569 | return -EKEYREJECTED; | |
570 | if (dnskey->dnskey.protocol != 3) | |
571 | return -EKEYREJECTED; | |
572 | ||
573 | if (!dnssec_algorithm_supported(dnskey->dnskey.algorithm)) | |
574 | return -EOPNOTSUPP; | |
575 | if (!dnssec_digest_supported(ds->ds.digest_type)) | |
576 | return -EOPNOTSUPP; | |
577 | ||
578 | if (dnskey->dnskey.algorithm != ds->ds.algorithm) | |
579 | return 0; | |
580 | if (dnssec_keytag(dnskey) != ds->ds.key_tag) | |
581 | return 0; | |
582 | ||
583 | switch (ds->ds.digest_type) { | |
584 | ||
585 | case DNSSEC_DIGEST_SHA1: | |
586 | ||
587 | if (ds->ds.digest_size != 20) | |
588 | return 0; | |
589 | ||
590 | gcry_md_open(&md, GCRY_MD_SHA1, 0); | |
591 | break; | |
592 | ||
593 | case DNSSEC_DIGEST_SHA256: | |
594 | ||
595 | if (ds->ds.digest_size != 32) | |
596 | return 0; | |
597 | ||
598 | gcry_md_open(&md, GCRY_MD_SHA256, 0); | |
599 | break; | |
600 | ||
601 | default: | |
602 | assert_not_reached("Unknown digest"); | |
603 | } | |
604 | ||
605 | if (!md) | |
606 | return -EIO; | |
607 | ||
608 | r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name)); | |
609 | if (r < 0) | |
610 | goto finish; | |
611 | ||
612 | gcry_md_write(md, owner_name, r); | |
613 | md_add_uint16(md, dnskey->dnskey.flags); | |
614 | md_add_uint8(md, dnskey->dnskey.protocol); | |
615 | md_add_uint8(md, dnskey->dnskey.algorithm); | |
616 | gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size); | |
617 | ||
618 | result = gcry_md_read(md, 0); | |
619 | if (!result) { | |
620 | r = -EIO; | |
621 | goto finish; | |
622 | } | |
623 | ||
624 | r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0; | |
625 | ||
626 | finish: | |
627 | gcry_md_close(md); | |
628 | return r; | |
629 | } |