xref: /netbsd-src/external/mpl/bind/dist/bin/tools/nsec3hash.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: nsec3hash.c,v 1.9 2025/01/26 16:25:10 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 #include <stdarg.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 
21 #include <isc/attributes.h>
22 #include <isc/base32.h>
23 #include <isc/buffer.h>
24 #include <isc/commandline.h>
25 #include <isc/file.h>
26 #include <isc/hex.h>
27 #include <isc/iterated_hash.h>
28 #include <isc/result.h>
29 #include <isc/string.h>
30 #include <isc/tls.h>
31 #include <isc/types.h>
32 #include <isc/util.h>
33 
34 #include <dns/fixedname.h>
35 #include <dns/name.h>
36 #include <dns/nsec3.h>
37 #include <dns/types.h>
38 
39 const char *program = "nsec3hash";
40 
41 noreturn static void
42 fatal(const char *format, ...);
43 
44 static void
45 fatal(const char *format, ...) {
46 	va_list args;
47 
48 	fprintf(stderr, "%s: ", program);
49 	va_start(args, format);
50 	vfprintf(stderr, format, args);
51 	va_end(args);
52 	fprintf(stderr, "\n");
53 	_exit(EXIT_FAILURE);
54 }
55 
56 static void
57 check_result(isc_result_t result, const char *message) {
58 	if (result != ISC_R_SUCCESS) {
59 		fatal("%s: %s", message, isc_result_totext(result));
60 	}
61 }
62 
63 static void
64 usage(void) {
65 	fprintf(stderr, "Usage: %s salt algorithm iterations domain\n",
66 		program);
67 	fprintf(stderr, "       %s -r algorithm flags iterations salt domain\n",
68 		program);
69 	exit(EXIT_FAILURE);
70 }
71 
72 typedef void
73 nsec3printer(unsigned int algo, unsigned int flags, unsigned int iters,
74 	     const char *saltstr, const char *domain, const char *digest);
75 
76 static void
77 nsec3hash(nsec3printer *nsec3print, const char *algostr, const char *flagstr,
78 	  const char *iterstr, const char *saltstr, const char *domain) {
79 	dns_fixedname_t fixed;
80 	dns_name_t *name;
81 	isc_buffer_t buffer;
82 	isc_region_t region;
83 	isc_result_t result;
84 	unsigned char hash[NSEC3_MAX_HASH_LENGTH];
85 	unsigned char salt[DNS_NSEC3_SALTSIZE];
86 	unsigned char text[1024];
87 	unsigned int hash_alg;
88 	unsigned int flags;
89 	unsigned int length;
90 	unsigned int iterations;
91 	unsigned int salt_length;
92 	const char dash[] = "-";
93 
94 	if (strcmp(saltstr, "-") == 0) {
95 		salt_length = 0;
96 		salt[0] = 0;
97 	} else {
98 		isc_buffer_init(&buffer, salt, sizeof(salt));
99 		result = isc_hex_decodestring(saltstr, &buffer);
100 		check_result(result, "isc_hex_decodestring(salt)");
101 		salt_length = isc_buffer_usedlength(&buffer);
102 		if (salt_length > DNS_NSEC3_SALTSIZE) {
103 			fatal("salt too long");
104 		}
105 		if (salt_length == 0) {
106 			saltstr = dash;
107 		}
108 	}
109 	hash_alg = atoi(algostr);
110 	if (hash_alg > 255U) {
111 		fatal("hash algorithm too large");
112 	}
113 	flags = flagstr == NULL ? 0 : atoi(flagstr);
114 	if (flags > 255U) {
115 		fatal("flags too large");
116 	}
117 	iterations = atoi(iterstr);
118 	if (iterations > 0xffffU) {
119 		fatal("iterations to large");
120 	}
121 
122 	name = dns_fixedname_initname(&fixed);
123 	isc_buffer_constinit(&buffer, domain, strlen(domain));
124 	isc_buffer_add(&buffer, strlen(domain));
125 	result = dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL);
126 	check_result(result, "dns_name_fromtext() failed");
127 
128 	dns_name_downcase(name, name, NULL);
129 	length = isc_iterated_hash(hash, hash_alg, iterations, salt,
130 				   salt_length, name->ndata, name->length);
131 	if (length == 0) {
132 		fatal("isc_iterated_hash failed");
133 	}
134 	region.base = hash;
135 	region.length = length;
136 	isc_buffer_init(&buffer, text, sizeof(text));
137 	isc_base32hexnp_totext(&region, 1, "", &buffer);
138 	isc_buffer_putuint8(&buffer, '\0');
139 
140 	nsec3print(hash_alg, flags, iterations, saltstr, domain, (char *)text);
141 }
142 
143 static void
144 nsec3hash_print(unsigned int algo, unsigned int flags, unsigned int iters,
145 		const char *saltstr, const char *domain, const char *digest) {
146 	UNUSED(flags);
147 	UNUSED(domain);
148 
149 	fprintf(stdout, "%s (salt=%s, hash=%u, iterations=%u)\n", digest,
150 		saltstr, algo, iters);
151 }
152 
153 static void
154 nsec3hash_rdata_print(unsigned int algo, unsigned int flags, unsigned int iters,
155 		      const char *saltstr, const char *domain,
156 		      const char *digest) {
157 	fprintf(stdout, "%s NSEC3 %u %u %u %s %s\n", domain, algo, flags, iters,
158 		saltstr, digest);
159 }
160 
161 int
162 main(int argc, char *argv[]) {
163 	bool rdata_format = false;
164 	int ch;
165 
166 	while ((ch = isc_commandline_parse(argc, argv, "-r")) != -1) {
167 		switch (ch) {
168 		case 'r':
169 			rdata_format = true;
170 			break;
171 		case '-':
172 			isc_commandline_index -= 1;
173 			goto skip;
174 		default:
175 			break;
176 		}
177 	}
178 
179 skip:
180 	argc -= isc_commandline_index;
181 	argv += isc_commandline_index;
182 
183 	if (rdata_format) {
184 		if (argc != 5) {
185 			usage();
186 		}
187 		nsec3hash(nsec3hash_rdata_print, argv[0], argv[1], argv[2],
188 			  argv[3], argv[4]);
189 	} else {
190 		if (argc != 4) {
191 			usage();
192 		}
193 		nsec3hash(nsec3hash_print, argv[1], NULL, argv[2], argv[0],
194 			  argv[3]);
195 	}
196 	return 0;
197 }
198