xref: /netbsd-src/external/mpl/bind/dist/lib/dns/hmac_link.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: hmac_link.c,v 1.10 2025/01/26 16:25:22 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0 AND ISC
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 /*
17  * Copyright (C) Network Associates, Inc.
18  *
19  * Permission to use, copy, modify, and/or distribute this software for any
20  * purpose with or without fee is hereby granted, provided that the above
21  * copyright notice and this permission notice appear in all copies.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
24  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
26  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
29  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30  */
31 
32 #include <arpa/inet.h>
33 #include <stdbool.h>
34 
35 #include <isc/buffer.h>
36 #include <isc/hmac.h>
37 #include <isc/lex.h>
38 #include <isc/md.h>
39 #include <isc/mem.h>
40 #include <isc/nonce.h>
41 #include <isc/random.h>
42 #include <isc/result.h>
43 #include <isc/safe.h>
44 #include <isc/string.h>
45 #include <isc/util.h>
46 
47 #include "dst_internal.h"
48 #include "dst_parse.h"
49 
50 #define ISC_MD_md5    ISC_MD_MD5
51 #define ISC_MD_sha1   ISC_MD_SHA1
52 #define ISC_MD_sha224 ISC_MD_SHA224
53 #define ISC_MD_sha256 ISC_MD_SHA256
54 #define ISC_MD_sha384 ISC_MD_SHA384
55 #define ISC_MD_sha512 ISC_MD_SHA512
56 
57 #define hmac_register_algorithm(alg)                                           \
58 	static isc_result_t hmac##alg##_createctx(dst_key_t *key,              \
59 						  dst_context_t *dctx) {       \
60 		return (hmac_createctx(ISC_MD_##alg, key, dctx));              \
61 	}                                                                      \
62 	static void hmac##alg##_destroyctx(dst_context_t *dctx) {              \
63 		hmac_destroyctx(dctx);                                         \
64 	}                                                                      \
65 	static isc_result_t hmac##alg##_adddata(dst_context_t *dctx,           \
66 						const isc_region_t *data) {    \
67 		return (hmac_adddata(dctx, data));                             \
68 	}                                                                      \
69 	static isc_result_t hmac##alg##_sign(dst_context_t *dctx,              \
70 					     isc_buffer_t *sig) {              \
71 		return (hmac_sign(dctx, sig));                                 \
72 	}                                                                      \
73 	static isc_result_t hmac##alg##_verify(dst_context_t *dctx,            \
74 					       const isc_region_t *sig) {      \
75 		return (hmac_verify(dctx, sig));                               \
76 	}                                                                      \
77 	static bool hmac##alg##_compare(const dst_key_t *key1,                 \
78 					const dst_key_t *key2) {               \
79 		return (hmac_compare(ISC_MD_##alg, key1, key2));               \
80 	}                                                                      \
81 	static isc_result_t hmac##alg##_generate(                              \
82 		dst_key_t *key, int pseudorandom_ok, void (*callback)(int)) {  \
83 		UNUSED(pseudorandom_ok);                                       \
84 		UNUSED(callback);                                              \
85 		return (hmac_generate(ISC_MD_##alg, key));                     \
86 	}                                                                      \
87 	static bool hmac##alg##_isprivate(const dst_key_t *key) {              \
88 		return (hmac_isprivate(key));                                  \
89 	}                                                                      \
90 	static void hmac##alg##_destroy(dst_key_t *key) { hmac_destroy(key); } \
91 	static isc_result_t hmac##alg##_todns(const dst_key_t *key,            \
92 					      isc_buffer_t *data) {            \
93 		return (hmac_todns(key, data));                                \
94 	}                                                                      \
95 	static isc_result_t hmac##alg##_fromdns(dst_key_t *key,                \
96 						isc_buffer_t *data) {          \
97 		return (hmac_fromdns(ISC_MD_##alg, key, data));                \
98 	}                                                                      \
99 	static isc_result_t hmac##alg##_tofile(const dst_key_t *key,           \
100 					       const char *directory) {        \
101 		return (hmac_tofile(ISC_MD_##alg, key, directory));            \
102 	}                                                                      \
103 	static isc_result_t hmac##alg##_parse(                                 \
104 		dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {            \
105 		const char *file = isc_lex_getsourcename(lexer);               \
106 		isc_result_t result;                                           \
107 		result = hmac_parse(ISC_MD_##alg, key, lexer, pub);            \
108 		if (result == ISC_R_SUCCESS && file != NULL) {                 \
109 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,       \
110 				      DNS_LOGMODULE_CRYPTO, ISC_LOG_WARNING,   \
111 				      "%s: Use of K* file pairs for HMAC is "  \
112 				      "deprecated\n",                          \
113 				      file);                                   \
114 		}                                                              \
115 		return (result);                                               \
116 	}                                                                      \
117 	static dst_func_t hmac##alg##_functions = {                            \
118 		hmac##alg##_createctx,                                         \
119 		NULL, /*%< createctx2 */                                       \
120 		hmac##alg##_destroyctx,                                        \
121 		hmac##alg##_adddata,                                           \
122 		hmac##alg##_sign,                                              \
123 		hmac##alg##_verify,                                            \
124 		NULL, /*%< verify2 */                                          \
125 		NULL, /*%< computesecret */                                    \
126 		hmac##alg##_compare,                                           \
127 		NULL, /*%< paramcompare */                                     \
128 		hmac##alg##_generate,                                          \
129 		hmac##alg##_isprivate,                                         \
130 		hmac##alg##_destroy,                                           \
131 		hmac##alg##_todns,                                             \
132 		hmac##alg##_fromdns,                                           \
133 		hmac##alg##_tofile,                                            \
134 		hmac##alg##_parse,                                             \
135 		NULL, /*%< cleanup */                                          \
136 		NULL, /*%< fromlabel */                                        \
137 		NULL, /*%< dump */                                             \
138 		NULL, /*%< restore */                                          \
139 	};                                                                     \
140 	isc_result_t dst__hmac##alg##_init(dst_func_t **funcp) {               \
141 		REQUIRE(funcp != NULL);                                        \
142 		if (*funcp == NULL) {                                          \
143 			isc_hmac_t *ctx = isc_hmac_new();                      \
144 			if (isc_hmac_init(ctx, "test", 4, ISC_MD_##alg) ==     \
145 			    ISC_R_SUCCESS)                                     \
146 			{                                                      \
147 				*funcp = &hmac##alg##_functions;               \
148 			}                                                      \
149 			isc_hmac_free(ctx);                                    \
150 		}                                                              \
151 		return (ISC_R_SUCCESS);                                        \
152 	}
153 
154 static isc_result_t
155 hmac_fromdns(const isc_md_type_t *type, dst_key_t *key, isc_buffer_t *data);
156 
157 struct dst_hmac_key {
158 	uint8_t key[ISC_MAX_BLOCK_SIZE];
159 };
160 
161 static isc_result_t
162 getkeybits(dst_key_t *key, struct dst_private_element *element) {
163 	uint16_t *bits = (uint16_t *)element->data;
164 
165 	if (element->length != 2) {
166 		return DST_R_INVALIDPRIVATEKEY;
167 	}
168 
169 	key->key_bits = ntohs(*bits);
170 
171 	return ISC_R_SUCCESS;
172 }
173 
174 static isc_result_t
175 hmac_createctx(const isc_md_type_t *type, const dst_key_t *key,
176 	       dst_context_t *dctx) {
177 	isc_result_t result;
178 	const dst_hmac_key_t *hkey = key->keydata.hmac_key;
179 	isc_hmac_t *ctx = isc_hmac_new(); /* Either returns or abort()s */
180 
181 	result = isc_hmac_init(ctx, hkey->key, isc_md_type_get_block_size(type),
182 			       type);
183 	if (result != ISC_R_SUCCESS) {
184 		isc_hmac_free(ctx);
185 		return DST_R_UNSUPPORTEDALG;
186 	}
187 
188 	dctx->ctxdata.hmac_ctx = ctx;
189 	return ISC_R_SUCCESS;
190 }
191 
192 static void
193 hmac_destroyctx(dst_context_t *dctx) {
194 	isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx;
195 	REQUIRE(ctx != NULL);
196 
197 	isc_hmac_free(ctx);
198 	dctx->ctxdata.hmac_ctx = NULL;
199 }
200 
201 static isc_result_t
202 hmac_adddata(const dst_context_t *dctx, const isc_region_t *data) {
203 	isc_result_t result;
204 	isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx;
205 
206 	REQUIRE(ctx != NULL);
207 
208 	result = isc_hmac_update(ctx, data->base, data->length);
209 	if (result != ISC_R_SUCCESS) {
210 		return DST_R_OPENSSLFAILURE;
211 	}
212 
213 	return ISC_R_SUCCESS;
214 }
215 
216 static isc_result_t
217 hmac_sign(const dst_context_t *dctx, isc_buffer_t *sig) {
218 	isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx;
219 	REQUIRE(ctx != NULL);
220 	unsigned char digest[ISC_MAX_MD_SIZE];
221 	unsigned int digestlen = sizeof(digest);
222 
223 	if (isc_hmac_final(ctx, digest, &digestlen) != ISC_R_SUCCESS) {
224 		return DST_R_OPENSSLFAILURE;
225 	}
226 
227 	if (isc_hmac_reset(ctx) != ISC_R_SUCCESS) {
228 		return DST_R_OPENSSLFAILURE;
229 	}
230 
231 	if (isc_buffer_availablelength(sig) < digestlen) {
232 		return ISC_R_NOSPACE;
233 	}
234 
235 	isc_buffer_putmem(sig, digest, digestlen);
236 
237 	return ISC_R_SUCCESS;
238 }
239 
240 static isc_result_t
241 hmac_verify(const dst_context_t *dctx, const isc_region_t *sig) {
242 	isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx;
243 	unsigned char digest[ISC_MAX_MD_SIZE];
244 	unsigned int digestlen = sizeof(digest);
245 
246 	REQUIRE(ctx != NULL);
247 
248 	if (isc_hmac_final(ctx, digest, &digestlen) != ISC_R_SUCCESS) {
249 		return DST_R_OPENSSLFAILURE;
250 	}
251 
252 	if (isc_hmac_reset(ctx) != ISC_R_SUCCESS) {
253 		return DST_R_OPENSSLFAILURE;
254 	}
255 
256 	if (sig->length > digestlen) {
257 		return DST_R_VERIFYFAILURE;
258 	}
259 
260 	return isc_safe_memequal(digest, sig->base, sig->length)
261 		       ? ISC_R_SUCCESS
262 		       : DST_R_VERIFYFAILURE;
263 }
264 
265 static bool
266 hmac_compare(const isc_md_type_t *type, const dst_key_t *key1,
267 	     const dst_key_t *key2) {
268 	dst_hmac_key_t *hkey1, *hkey2;
269 
270 	hkey1 = key1->keydata.hmac_key;
271 	hkey2 = key2->keydata.hmac_key;
272 
273 	if (hkey1 == NULL && hkey2 == NULL) {
274 		return true;
275 	} else if (hkey1 == NULL || hkey2 == NULL) {
276 		return false;
277 	}
278 
279 	return isc_safe_memequal(hkey1->key, hkey2->key,
280 				 isc_md_type_get_block_size(type));
281 }
282 
283 static isc_result_t
284 hmac_generate(const isc_md_type_t *type, dst_key_t *key) {
285 	isc_buffer_t b;
286 	isc_result_t ret;
287 	unsigned int bytes, len;
288 	unsigned char data[ISC_MAX_MD_SIZE] = { 0 };
289 
290 	len = isc_md_type_get_block_size(type);
291 
292 	bytes = (key->key_size + 7) / 8;
293 
294 	if (bytes > len) {
295 		bytes = len;
296 		key->key_size = len * 8;
297 	}
298 
299 	isc_nonce_buf(data, bytes);
300 
301 	isc_buffer_init(&b, data, bytes);
302 	isc_buffer_add(&b, bytes);
303 
304 	ret = hmac_fromdns(type, key, &b);
305 
306 	isc_safe_memwipe(data, sizeof(data));
307 
308 	return ret;
309 }
310 
311 static bool
312 hmac_isprivate(const dst_key_t *key) {
313 	UNUSED(key);
314 	return true;
315 }
316 
317 static void
318 hmac_destroy(dst_key_t *key) {
319 	dst_hmac_key_t *hkey = key->keydata.hmac_key;
320 	isc_safe_memwipe(hkey, sizeof(*hkey));
321 	isc_mem_put(key->mctx, hkey, sizeof(*hkey));
322 	key->keydata.hmac_key = NULL;
323 }
324 
325 static isc_result_t
326 hmac_todns(const dst_key_t *key, isc_buffer_t *data) {
327 	REQUIRE(key != NULL && key->keydata.hmac_key != NULL);
328 	dst_hmac_key_t *hkey = key->keydata.hmac_key;
329 	unsigned int bytes;
330 
331 	bytes = (key->key_size + 7) / 8;
332 	if (isc_buffer_availablelength(data) < bytes) {
333 		return ISC_R_NOSPACE;
334 	}
335 	isc_buffer_putmem(data, hkey->key, bytes);
336 
337 	return ISC_R_SUCCESS;
338 }
339 
340 static isc_result_t
341 hmac_fromdns(const isc_md_type_t *type, dst_key_t *key, isc_buffer_t *data) {
342 	dst_hmac_key_t *hkey;
343 	unsigned int keylen;
344 	isc_region_t r;
345 
346 	isc_buffer_remainingregion(data, &r);
347 	if (r.length == 0) {
348 		return ISC_R_SUCCESS;
349 	}
350 
351 	hkey = isc_mem_get(key->mctx, sizeof(dst_hmac_key_t));
352 
353 	memset(hkey->key, 0, sizeof(hkey->key));
354 
355 	/* Hash the key if the key is longer then chosen MD block size */
356 	if (r.length > (unsigned int)isc_md_type_get_block_size(type)) {
357 		if (isc_md(type, r.base, r.length, hkey->key, &keylen) !=
358 		    ISC_R_SUCCESS)
359 		{
360 			isc_mem_put(key->mctx, hkey, sizeof(dst_hmac_key_t));
361 			return DST_R_OPENSSLFAILURE;
362 		}
363 	} else {
364 		memmove(hkey->key, r.base, r.length);
365 		keylen = r.length;
366 	}
367 
368 	key->key_size = keylen * 8;
369 	key->keydata.hmac_key = hkey;
370 
371 	isc_buffer_forward(data, r.length);
372 
373 	return ISC_R_SUCCESS;
374 }
375 
376 static int
377 hmac__get_tag_key(const isc_md_type_t *type) {
378 	if (type == ISC_MD_MD5) {
379 		return TAG_HMACMD5_KEY;
380 	} else if (type == ISC_MD_SHA1) {
381 		return TAG_HMACSHA1_KEY;
382 	} else if (type == ISC_MD_SHA224) {
383 		return TAG_HMACSHA224_KEY;
384 	} else if (type == ISC_MD_SHA256) {
385 		return TAG_HMACSHA256_KEY;
386 	} else if (type == ISC_MD_SHA384) {
387 		return TAG_HMACSHA384_KEY;
388 	} else if (type == ISC_MD_SHA512) {
389 		return TAG_HMACSHA512_KEY;
390 	} else {
391 		UNREACHABLE();
392 	}
393 }
394 
395 static int
396 hmac__get_tag_bits(const isc_md_type_t *type) {
397 	if (type == ISC_MD_MD5) {
398 		return TAG_HMACMD5_BITS;
399 	} else if (type == ISC_MD_SHA1) {
400 		return TAG_HMACSHA1_BITS;
401 	} else if (type == ISC_MD_SHA224) {
402 		return TAG_HMACSHA224_BITS;
403 	} else if (type == ISC_MD_SHA256) {
404 		return TAG_HMACSHA256_BITS;
405 	} else if (type == ISC_MD_SHA384) {
406 		return TAG_HMACSHA384_BITS;
407 	} else if (type == ISC_MD_SHA512) {
408 		return TAG_HMACSHA512_BITS;
409 	} else {
410 		UNREACHABLE();
411 	}
412 }
413 
414 static isc_result_t
415 hmac_tofile(const isc_md_type_t *type, const dst_key_t *key,
416 	    const char *directory) {
417 	dst_hmac_key_t *hkey;
418 	dst_private_t priv;
419 	int bytes = (key->key_size + 7) / 8;
420 	uint16_t bits;
421 
422 	if (key->keydata.hmac_key == NULL) {
423 		return DST_R_NULLKEY;
424 	}
425 
426 	if (key->external) {
427 		return DST_R_EXTERNALKEY;
428 	}
429 
430 	hkey = key->keydata.hmac_key;
431 
432 	priv.elements[0].tag = hmac__get_tag_key(type);
433 	priv.elements[0].length = bytes;
434 	priv.elements[0].data = hkey->key;
435 
436 	bits = htons(key->key_bits);
437 
438 	priv.elements[1].tag = hmac__get_tag_bits(type);
439 	priv.elements[1].length = sizeof(bits);
440 	priv.elements[1].data = (uint8_t *)&bits;
441 
442 	priv.nelements = 2;
443 
444 	return dst__privstruct_writefile(key, &priv, directory);
445 }
446 
447 static int
448 hmac__to_dst_alg(const isc_md_type_t *type) {
449 	if (type == ISC_MD_MD5) {
450 		return DST_ALG_HMACMD5;
451 	} else if (type == ISC_MD_SHA1) {
452 		return DST_ALG_HMACSHA1;
453 	} else if (type == ISC_MD_SHA224) {
454 		return DST_ALG_HMACSHA224;
455 	} else if (type == ISC_MD_SHA256) {
456 		return DST_ALG_HMACSHA256;
457 	} else if (type == ISC_MD_SHA384) {
458 		return DST_ALG_HMACSHA384;
459 	} else if (type == ISC_MD_SHA512) {
460 		return DST_ALG_HMACSHA512;
461 	} else {
462 		UNREACHABLE();
463 	}
464 }
465 
466 static isc_result_t
467 hmac_parse(const isc_md_type_t *type, dst_key_t *key, isc_lex_t *lexer,
468 	   dst_key_t *pub) {
469 	dst_private_t priv;
470 	isc_result_t result, tresult;
471 	isc_buffer_t b;
472 	isc_mem_t *mctx = key->mctx;
473 	unsigned int i;
474 
475 	UNUSED(pub);
476 	/* read private key file */
477 	result = dst__privstruct_parse(key, hmac__to_dst_alg(type), lexer, mctx,
478 				       &priv);
479 	if (result != ISC_R_SUCCESS) {
480 		return result;
481 	}
482 
483 	if (key->external) {
484 		result = DST_R_EXTERNALKEY;
485 	}
486 
487 	key->key_bits = 0;
488 	for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
489 		switch (priv.elements[i].tag) {
490 		case TAG_HMACMD5_KEY:
491 		case TAG_HMACSHA1_KEY:
492 		case TAG_HMACSHA224_KEY:
493 		case TAG_HMACSHA256_KEY:
494 		case TAG_HMACSHA384_KEY:
495 		case TAG_HMACSHA512_KEY:
496 			isc_buffer_init(&b, priv.elements[i].data,
497 					priv.elements[i].length);
498 			isc_buffer_add(&b, priv.elements[i].length);
499 			tresult = hmac_fromdns(type, key, &b);
500 			if (tresult != ISC_R_SUCCESS) {
501 				result = tresult;
502 			}
503 			break;
504 		case TAG_HMACMD5_BITS:
505 		case TAG_HMACSHA1_BITS:
506 		case TAG_HMACSHA224_BITS:
507 		case TAG_HMACSHA256_BITS:
508 		case TAG_HMACSHA384_BITS:
509 		case TAG_HMACSHA512_BITS:
510 			tresult = getkeybits(key, &priv.elements[i]);
511 			if (tresult != ISC_R_SUCCESS) {
512 				result = tresult;
513 			}
514 			break;
515 		default:
516 			result = DST_R_INVALIDPRIVATEKEY;
517 			break;
518 		}
519 	}
520 	dst__privstruct_free(&priv, mctx);
521 	isc_safe_memwipe(&priv, sizeof(priv));
522 	return result;
523 }
524 
525 hmac_register_algorithm(md5);
526 hmac_register_algorithm(sha1);
527 hmac_register_algorithm(sha224);
528 hmac_register_algorithm(sha256);
529 hmac_register_algorithm(sha384);
530 hmac_register_algorithm(sha512);
531 
532 /*! \file */
533