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