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