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