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