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