1 /* $NetBSD: iterated_hash.c,v 1.9 2025/01/26 16:25:37 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 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 #include <stdbool.h> 17 #include <stdio.h> 18 19 #include <openssl/err.h> 20 #include <openssl/opensslv.h> 21 22 #include <isc/iterated_hash.h> 23 #include <isc/thread.h> 24 #include <isc/util.h> 25 26 #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 27 28 #include <openssl/sha.h> 29 30 int 31 isc_iterated_hash(unsigned char *out, const unsigned int hashalg, 32 const int iterations, const unsigned char *salt, 33 const int saltlength, const unsigned char *in, 34 const int inlength) { 35 REQUIRE(out != NULL); 36 37 int n = 0; 38 size_t len; 39 const unsigned char *buf; 40 SHA_CTX ctx; 41 42 if (hashalg != 1) { 43 return 0; 44 } 45 46 buf = in; 47 len = inlength; 48 49 do { 50 if (SHA1_Init(&ctx) != 1) { 51 ERR_clear_error(); 52 return 0; 53 } 54 55 if (SHA1_Update(&ctx, buf, len) != 1) { 56 ERR_clear_error(); 57 return 0; 58 } 59 60 if (SHA1_Update(&ctx, salt, saltlength) != 1) { 61 ERR_clear_error(); 62 return 0; 63 } 64 65 if (SHA1_Final(out, &ctx) != 1) { 66 ERR_clear_error(); 67 return 0; 68 } 69 70 buf = out; 71 len = SHA_DIGEST_LENGTH; 72 } while (n++ < iterations); 73 74 return SHA_DIGEST_LENGTH; 75 } 76 77 void 78 isc__iterated_hash_initialize(void) { 79 /* empty */ 80 } 81 82 void 83 isc__iterated_hash_shutdown(void) { 84 /* empty */ 85 } 86 87 #else /* HAVE_SHA1_INIT */ 88 89 #include <openssl/evp.h> 90 91 static thread_local bool initialized = false; 92 static thread_local EVP_MD_CTX *mdctx = NULL; 93 static thread_local EVP_MD_CTX *basectx = NULL; 94 static thread_local EVP_MD *md = NULL; 95 96 int 97 isc_iterated_hash(unsigned char *out, const unsigned int hashalg, 98 const int iterations, const unsigned char *salt, 99 const int saltlength, const unsigned char *in, 100 const int inlength) { 101 REQUIRE(out != NULL); 102 REQUIRE(mdctx != NULL); 103 REQUIRE(basectx != NULL); 104 105 int n = 0; 106 size_t len; 107 unsigned int outlength = 0; 108 const unsigned char *buf; 109 110 if (hashalg != 1) { 111 return 0; 112 } 113 114 buf = in; 115 len = inlength; 116 do { 117 if (EVP_MD_CTX_copy_ex(mdctx, basectx) != 1) { 118 goto fail; 119 } 120 121 if (EVP_DigestUpdate(mdctx, buf, len) != 1) { 122 goto fail; 123 } 124 125 if (EVP_DigestUpdate(mdctx, salt, saltlength) != 1) { 126 goto fail; 127 } 128 129 if (EVP_DigestFinal_ex(mdctx, out, &outlength) != 1) { 130 goto fail; 131 } 132 133 buf = out; 134 len = outlength; 135 } while (n++ < iterations); 136 137 return outlength; 138 139 fail: 140 ERR_clear_error(); 141 return 0; 142 } 143 144 void 145 isc__iterated_hash_initialize(void) { 146 if (initialized) { 147 return; 148 } 149 150 basectx = EVP_MD_CTX_new(); 151 INSIST(basectx != NULL); 152 mdctx = EVP_MD_CTX_new(); 153 INSIST(mdctx != NULL); 154 md = EVP_MD_fetch(NULL, "SHA1", NULL); 155 INSIST(md != NULL); 156 157 RUNTIME_CHECK(EVP_DigestInit_ex(basectx, md, NULL) == 1); 158 initialized = true; 159 } 160 161 void 162 isc__iterated_hash_shutdown(void) { 163 if (!initialized) { 164 return; 165 } 166 167 REQUIRE(mdctx != NULL); 168 EVP_MD_CTX_free(mdctx); 169 mdctx = NULL; 170 REQUIRE(basectx != NULL); 171 EVP_MD_CTX_free(basectx); 172 basectx = NULL; 173 EVP_MD_free(md); 174 md = NULL; 175 176 initialized = false; 177 } 178 179 #endif /* HAVE_SHA1_INIT */ 180