1/*	$NetBSD: ds_43.c,v 1.11 2024/02/21 22:52:12 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/* RFC3658 */
17
18#ifndef RDATA_GENERIC_DS_43_C
19#define RDATA_GENERIC_DS_43_C
20
21#define RRTYPE_DS_ATTRIBUTES                                        \
22	(DNS_RDATATYPEATTR_DNSSEC | DNS_RDATATYPEATTR_ZONECUTAUTH | \
23	 DNS_RDATATYPEATTR_ATPARENT)
24
25#include <isc/md.h>
26
27#include <dns/ds.h>
28
29static isc_result_t
30generic_fromtext_ds(ARGS_FROMTEXT) {
31	isc_token_t token;
32	unsigned char c;
33	int length;
34
35	UNUSED(type);
36	UNUSED(rdclass);
37	UNUSED(origin);
38	UNUSED(options);
39	UNUSED(callbacks);
40
41	/*
42	 * Key tag.
43	 */
44	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
45				      false));
46	if (token.value.as_ulong > 0xffffU) {
47		RETTOK(ISC_R_RANGE);
48	}
49	RETERR(uint16_tobuffer(token.value.as_ulong, target));
50
51	/*
52	 * Algorithm.
53	 */
54	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
55				      false));
56	RETTOK(dns_secalg_fromtext(&c, &token.value.as_textregion));
57	RETERR(mem_tobuffer(target, &c, 1));
58
59	/*
60	 * Digest type.
61	 */
62	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
63				      false));
64	RETTOK(dns_dsdigest_fromtext(&c, &token.value.as_textregion));
65	RETERR(mem_tobuffer(target, &c, 1));
66
67	/*
68	 * Digest.
69	 */
70	switch (c) {
71	case DNS_DSDIGEST_SHA1:
72		length = ISC_SHA1_DIGESTLENGTH;
73		break;
74	case DNS_DSDIGEST_SHA256:
75		length = ISC_SHA256_DIGESTLENGTH;
76		break;
77	case DNS_DSDIGEST_SHA384:
78		length = ISC_SHA384_DIGESTLENGTH;
79		break;
80	default:
81		length = -2;
82		break;
83	}
84	return (isc_hex_tobuffer(lexer, target, length));
85}
86
87static isc_result_t
88fromtext_ds(ARGS_FROMTEXT) {
89	REQUIRE(type == dns_rdatatype_ds);
90
91	return (generic_fromtext_ds(CALL_FROMTEXT));
92}
93
94static isc_result_t
95generic_totext_ds(ARGS_TOTEXT) {
96	isc_region_t sr;
97	char buf[sizeof("64000 ")];
98	unsigned int n;
99
100	REQUIRE(rdata->length != 0);
101
102	UNUSED(tctx);
103
104	dns_rdata_toregion(rdata, &sr);
105
106	/*
107	 * Key tag.
108	 */
109	n = uint16_fromregion(&sr);
110	isc_region_consume(&sr, 2);
111	snprintf(buf, sizeof(buf), "%u ", n);
112	RETERR(str_totext(buf, target));
113
114	/*
115	 * Algorithm.
116	 */
117	n = uint8_fromregion(&sr);
118	isc_region_consume(&sr, 1);
119	snprintf(buf, sizeof(buf), "%u ", n);
120	RETERR(str_totext(buf, target));
121
122	/*
123	 * Digest type.
124	 */
125	n = uint8_fromregion(&sr);
126	isc_region_consume(&sr, 1);
127	snprintf(buf, sizeof(buf), "%u", n);
128	RETERR(str_totext(buf, target));
129
130	/*
131	 * Digest.
132	 */
133	if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
134		RETERR(str_totext(" (", target));
135	}
136	RETERR(str_totext(tctx->linebreak, target));
137	if ((tctx->flags & DNS_STYLEFLAG_NOCRYPTO) == 0) {
138		if (tctx->width == 0) { /* No splitting */
139			RETERR(isc_hex_totext(&sr, 0, "", target));
140		} else {
141			RETERR(isc_hex_totext(&sr, tctx->width - 2,
142					      tctx->linebreak, target));
143		}
144	} else {
145		RETERR(str_totext("[omitted]", target));
146	}
147	if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
148		RETERR(str_totext(" )", target));
149	}
150	return (ISC_R_SUCCESS);
151}
152
153static isc_result_t
154totext_ds(ARGS_TOTEXT) {
155	REQUIRE(rdata != NULL);
156	REQUIRE(rdata->type == dns_rdatatype_ds);
157
158	return (generic_totext_ds(CALL_TOTEXT));
159}
160
161static isc_result_t
162generic_fromwire_ds(ARGS_FROMWIRE) {
163	isc_region_t sr;
164
165	UNUSED(type);
166	UNUSED(rdclass);
167	UNUSED(dctx);
168	UNUSED(options);
169
170	isc_buffer_activeregion(source, &sr);
171
172	/*
173	 * Check digest lengths if we know them.
174	 */
175	if (sr.length < 5 ||
176	    (sr.base[3] == DNS_DSDIGEST_SHA1 &&
177	     sr.length < 4 + ISC_SHA1_DIGESTLENGTH) ||
178	    (sr.base[3] == DNS_DSDIGEST_SHA256 &&
179	     sr.length < 4 + ISC_SHA256_DIGESTLENGTH) ||
180	    (sr.base[3] == DNS_DSDIGEST_SHA384 &&
181	     sr.length < 4 + ISC_SHA384_DIGESTLENGTH))
182	{
183		return (ISC_R_UNEXPECTEDEND);
184	}
185
186	/*
187	 * Only copy digest lengths if we know them.
188	 * If there is extra data dns_rdata_fromwire() will
189	 * detect that.
190	 */
191	if (sr.base[3] == DNS_DSDIGEST_SHA1) {
192		sr.length = 4 + ISC_SHA1_DIGESTLENGTH;
193	} else if (sr.base[3] == DNS_DSDIGEST_SHA256) {
194		sr.length = 4 + ISC_SHA256_DIGESTLENGTH;
195	} else if (sr.base[3] == DNS_DSDIGEST_SHA384) {
196		sr.length = 4 + ISC_SHA384_DIGESTLENGTH;
197	}
198
199	isc_buffer_forward(source, sr.length);
200	return (mem_tobuffer(target, sr.base, sr.length));
201}
202
203static isc_result_t
204fromwire_ds(ARGS_FROMWIRE) {
205	REQUIRE(type == dns_rdatatype_ds);
206
207	return (generic_fromwire_ds(CALL_FROMWIRE));
208}
209
210static isc_result_t
211towire_ds(ARGS_TOWIRE) {
212	isc_region_t sr;
213
214	REQUIRE(rdata->type == dns_rdatatype_ds);
215	REQUIRE(rdata->length != 0);
216
217	UNUSED(cctx);
218
219	dns_rdata_toregion(rdata, &sr);
220	return (mem_tobuffer(target, sr.base, sr.length));
221}
222
223static int
224compare_ds(ARGS_COMPARE) {
225	isc_region_t r1;
226	isc_region_t r2;
227
228	REQUIRE(rdata1->type == rdata2->type);
229	REQUIRE(rdata1->rdclass == rdata2->rdclass);
230	REQUIRE(rdata1->type == dns_rdatatype_ds);
231	REQUIRE(rdata1->length != 0);
232	REQUIRE(rdata2->length != 0);
233
234	dns_rdata_toregion(rdata1, &r1);
235	dns_rdata_toregion(rdata2, &r2);
236	return (isc_region_compare(&r1, &r2));
237}
238
239static isc_result_t
240generic_fromstruct_ds(ARGS_FROMSTRUCT) {
241	dns_rdata_ds_t *ds = source;
242
243	REQUIRE(ds != NULL);
244	REQUIRE(ds->common.rdtype == type);
245	REQUIRE(ds->common.rdclass == rdclass);
246
247	UNUSED(type);
248	UNUSED(rdclass);
249
250	switch (ds->digest_type) {
251	case DNS_DSDIGEST_SHA1:
252		REQUIRE(ds->length == ISC_SHA1_DIGESTLENGTH);
253		break;
254	case DNS_DSDIGEST_SHA256:
255		REQUIRE(ds->length == ISC_SHA256_DIGESTLENGTH);
256		break;
257	case DNS_DSDIGEST_SHA384:
258		REQUIRE(ds->length == ISC_SHA384_DIGESTLENGTH);
259		break;
260	}
261
262	RETERR(uint16_tobuffer(ds->key_tag, target));
263	RETERR(uint8_tobuffer(ds->algorithm, target));
264	RETERR(uint8_tobuffer(ds->digest_type, target));
265
266	return (mem_tobuffer(target, ds->digest, ds->length));
267}
268
269static isc_result_t
270fromstruct_ds(ARGS_FROMSTRUCT) {
271	REQUIRE(type == dns_rdatatype_ds);
272
273	return (generic_fromstruct_ds(CALL_FROMSTRUCT));
274}
275
276static isc_result_t
277generic_tostruct_ds(ARGS_TOSTRUCT) {
278	dns_rdata_ds_t *ds = target;
279	isc_region_t region;
280
281	REQUIRE(ds != NULL);
282	REQUIRE(rdata->length != 0);
283	REQUIRE(ds->common.rdtype == rdata->type);
284	REQUIRE(ds->common.rdclass == rdata->rdclass);
285	REQUIRE(!ISC_LINK_LINKED(&ds->common, link));
286
287	dns_rdata_toregion(rdata, &region);
288
289	ds->key_tag = uint16_fromregion(&region);
290	isc_region_consume(&region, 2);
291	ds->algorithm = uint8_fromregion(&region);
292	isc_region_consume(&region, 1);
293	ds->digest_type = uint8_fromregion(&region);
294	isc_region_consume(&region, 1);
295	ds->length = region.length;
296
297	ds->digest = mem_maybedup(mctx, region.base, region.length);
298	if (ds->digest == NULL) {
299		return (ISC_R_NOMEMORY);
300	}
301
302	ds->mctx = mctx;
303	return (ISC_R_SUCCESS);
304}
305
306static isc_result_t
307tostruct_ds(ARGS_TOSTRUCT) {
308	dns_rdata_ds_t *ds = target;
309
310	REQUIRE(rdata->type == dns_rdatatype_ds);
311	REQUIRE(ds != NULL);
312
313	ds->common.rdclass = rdata->rdclass;
314	ds->common.rdtype = rdata->type;
315	ISC_LINK_INIT(&ds->common, link);
316
317	return (generic_tostruct_ds(CALL_TOSTRUCT));
318}
319
320static void
321freestruct_ds(ARGS_FREESTRUCT) {
322	dns_rdata_ds_t *ds = source;
323
324	REQUIRE(ds != NULL);
325	REQUIRE(ds->common.rdtype == dns_rdatatype_ds);
326
327	if (ds->mctx == NULL) {
328		return;
329	}
330
331	if (ds->digest != NULL) {
332		isc_mem_free(ds->mctx, ds->digest);
333	}
334	ds->mctx = NULL;
335}
336
337static isc_result_t
338additionaldata_ds(ARGS_ADDLDATA) {
339	REQUIRE(rdata->type == dns_rdatatype_ds);
340
341	UNUSED(rdata);
342	UNUSED(owner);
343	UNUSED(add);
344	UNUSED(arg);
345
346	return (ISC_R_SUCCESS);
347}
348
349static isc_result_t
350digest_ds(ARGS_DIGEST) {
351	isc_region_t r;
352
353	REQUIRE(rdata->type == dns_rdatatype_ds);
354
355	dns_rdata_toregion(rdata, &r);
356
357	return ((digest)(arg, &r));
358}
359
360static bool
361checkowner_ds(ARGS_CHECKOWNER) {
362	REQUIRE(type == dns_rdatatype_ds);
363
364	UNUSED(name);
365	UNUSED(type);
366	UNUSED(rdclass);
367	UNUSED(wildcard);
368
369	return (true);
370}
371
372static bool
373checknames_ds(ARGS_CHECKNAMES) {
374	REQUIRE(rdata->type == dns_rdatatype_ds);
375
376	UNUSED(rdata);
377	UNUSED(owner);
378	UNUSED(bad);
379
380	return (true);
381}
382
383static int
384casecompare_ds(ARGS_COMPARE) {
385	return (compare_ds(rdata1, rdata2));
386}
387
388#endif /* RDATA_GENERIC_DS_43_C */
389