hmac_link.c revision 1.1
1/*	$NetBSD: hmac_link.c,v 1.1 2024/02/18 20:57:32 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
145static isc_result_t
146hmac_fromdns(const isc_md_type_t *type, dst_key_t *key, isc_buffer_t *data);
147
148struct dst_hmac_key {
149	uint8_t key[ISC_MAX_BLOCK_SIZE];
150};
151
152static isc_result_t
153getkeybits(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
165static isc_result_t
166hmac_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
183static void
184hmac_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
192static isc_result_t
193hmac_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
207static isc_result_t
208hmac_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
231static isc_result_t
232hmac_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
256static bool
257hmac_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
274static isc_result_t
275hmac_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
302static bool
303hmac_isprivate(const dst_key_t *key) {
304	UNUSED(key);
305	return (true);
306}
307
308static void
309hmac_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
316static isc_result_t
317hmac_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
331static isc_result_t
332hmac_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
367static int
368hmac__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
386static int
387hmac__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
405static isc_result_t
406hmac_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
438static int
439hmac__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
457static isc_result_t
458hmac_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
516hmac_register_algorithm(md5);
517hmac_register_algorithm(sha1);
518hmac_register_algorithm(sha224);
519hmac_register_algorithm(sha256);
520hmac_register_algorithm(sha384);
521hmac_register_algorithm(sha512);
522
523/*! \file */
524