Coverage Report

Created: 2026-06-15 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/isc/crypto/ossl3.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
#include <stdbool.h>
15
#include <stdint.h>
16
17
#include <openssl/core_names.h>
18
#include <openssl/crypto.h>
19
#include <openssl/err.h>
20
#include <openssl/evp.h>
21
#include <openssl/provider.h>
22
#include <openssl/rand.h>
23
#include <openssl/ssl.h>
24
25
#include <isc/buffer.h>
26
#include <isc/crypto.h>
27
#include <isc/hmac.h>
28
#include <isc/log.h>
29
#include <isc/magic.h>
30
#include <isc/md.h>
31
#include <isc/mem.h>
32
#include <isc/ossl_wrap.h>
33
#include <isc/region.h>
34
#include <isc/safe.h>
35
#include <isc/util.h>
36
37
struct isc_hmac_key {
38
  uint32_t magic;
39
  uint32_t len;
40
  isc_mem_t *mctx;
41
  const OSSL_PARAM *params;
42
  uint8_t secret[];
43
};
44
45
constexpr uint32_t hmac_key_magic = ISC_MAGIC('H', 'M', 'A', 'C');
46
47
static OSSL_PROVIDER *base = NULL, *fips = NULL;
48
49
static EVP_MAC *evp_hmac = NULL;
50
51
static OSSL_PARAM md_to_hmac_params[ISC_MD_MAX][2] = {
52
  [ISC_MD_UNKNOWN] = { OSSL_PARAM_END },
53
  [ISC_MD_MD5] = {
54
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("MD5"), sizeof("MD5") - 1),
55
    OSSL_PARAM_END,
56
  },
57
  [ISC_MD_SHA1] = {
58
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA1"), sizeof("SHA1") - 1),
59
    OSSL_PARAM_END,
60
  },
61
  [ISC_MD_SHA224] = {
62
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-224"), sizeof("SHA2-224") - 1),
63
    OSSL_PARAM_END,
64
  },
65
  [ISC_MD_SHA256] = {
66
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-256"), sizeof("SHA2-256") - 1),
67
    OSSL_PARAM_END,
68
  },
69
  [ISC_MD_SHA384] = {
70
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-384"), sizeof("SHA2-384") - 1),
71
    OSSL_PARAM_END,
72
  },
73
  [ISC_MD_SHA512] = {
74
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-512"), sizeof("SHA2-512") - 1),
75
    OSSL_PARAM_END,
76
  },
77
};
78
79
#define md_register_algorithm(alg)                                             \
80
132
  {                                                                      \
81
132
    REQUIRE(isc__crypto_md[ISC_MD_##alg] == NULL);                 \
82
132
    isc__crypto_md[ISC_MD_##alg] = EVP_MD_fetch(NULL, #alg, NULL); \
83
132
    if (isc__crypto_md[ISC_MD_##alg] == NULL) {                    \
84
0
      ERR_clear_error();                                     \
85
0
    }                                                              \
86
132
  }
87
88
static isc_result_t
89
22
register_algorithms(void) {
90
22
  if (!isc_crypto_fips_mode()) {
91
22
    md_register_algorithm(MD5);
92
22
  }
93
94
22
  md_register_algorithm(SHA1);
95
22
  md_register_algorithm(SHA224);
96
22
  md_register_algorithm(SHA256);
97
22
  md_register_algorithm(SHA384);
98
22
  md_register_algorithm(SHA512);
99
100
  /* We _must_ have HMAC */
101
22
  evp_hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
102
22
  if (evp_hmac == NULL) {
103
0
    ERR_clear_error();
104
0
    FATAL_ERROR("OpenSSL failed to find an HMAC implementation. "
105
0
          "Please make sure the default provider has an "
106
0
          "EVP_MAC-HMAC implementation");
107
0
  }
108
109
22
  return ISC_R_SUCCESS;
110
22
}
111
112
static void
113
0
unregister_algorithms(void) {
114
0
  for (size_t i = 0; i < ISC_MD_MAX; i++) {
115
0
    if (isc__crypto_md[i] != NULL) {
116
0
      EVP_MD_free(isc__crypto_md[i]);
117
0
      isc__crypto_md[i] = NULL;
118
0
    }
119
0
  }
120
121
0
  INSIST(evp_hmac != NULL);
122
0
  EVP_MAC_free(evp_hmac);
123
0
  evp_hmac = NULL;
124
0
}
125
126
#undef md_register_algorithm
127
128
/*
129
 * HMAC
130
 */
131
132
/*
133
 * Do not call EVP_Q_mac or HMAC (since it calls EVP_Q_mac internally)
134
 *
135
 * Each invocation of the EVP_Q_mac function causes an explicit fetch.
136
 */
137
isc_result_t
138
isc_hmac(isc_md_type_t type, const void *key, const size_t keylen,
139
   const unsigned char *buf, const size_t len, unsigned char *digest,
140
132
   unsigned int *digestlen) {
141
132
  EVP_MAC_CTX *ctx;
142
132
  size_t maclen;
143
144
132
  REQUIRE(type < ISC_MD_MAX);
145
146
132
  if (isc__crypto_md[type] == NULL) {
147
0
    return ISC_R_NOTIMPLEMENTED;
148
0
  }
149
150
132
  ctx = EVP_MAC_CTX_new(evp_hmac);
151
132
  RUNTIME_CHECK(ctx != NULL);
152
153
132
  if (EVP_MAC_init(ctx, key, keylen, md_to_hmac_params[type]) != 1) {
154
0
    goto fail;
155
0
  }
156
157
132
  if (EVP_MAC_update(ctx, buf, len) != 1) {
158
0
    goto fail;
159
0
  }
160
161
132
  maclen = *digestlen;
162
132
  if (EVP_MAC_final(ctx, digest, &maclen, maclen) != 1) {
163
0
    goto fail;
164
0
  }
165
166
132
  *digestlen = maclen;
167
168
132
  EVP_MAC_CTX_free(ctx);
169
132
  return ISC_R_SUCCESS;
170
171
0
fail:
172
0
  ERR_clear_error();
173
0
  EVP_MAC_CTX_free(ctx);
174
0
  return ISC_R_CRYPTOFAILURE;
175
132
}
176
177
/*
178
 * You do not need to process the key to fit the block size.
179
 *
180
 * https://github.com/openssl/openssl/blob/925e4fba1098036e8f8d22652cff6f64c5c7d571/crypto/hmac/hmac.c#L61-L80
181
 */
182
isc_result_t
183
isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len,
184
2
        isc_mem_t *mctx, isc_hmac_key_t **keyp) {
185
2
  isc_hmac_key_t *key;
186
2
  uint8_t digest[ISC_MAX_MD_SIZE];
187
2
  unsigned int digest_len = sizeof(digest);
188
2
  size_t key_len;
189
190
2
  REQUIRE(keyp != NULL && *keyp == NULL);
191
2
  REQUIRE(type < ISC_MD_MAX);
192
193
2
  if (isc__crypto_md[type] == NULL) {
194
0
    return ISC_R_NOTIMPLEMENTED;
195
0
  }
196
197
2
  if (len > (size_t)EVP_MD_block_size(isc__crypto_md[type])) {
198
0
    RETERR(isc_md(type, secret, len, digest, &digest_len));
199
0
    secret = digest;
200
0
    key_len = digest_len;
201
2
  } else {
202
2
    key_len = len;
203
2
  }
204
205
2
  key = isc_mem_get(mctx, STRUCT_FLEX_SIZE(key, secret, key_len));
206
2
  *key = (isc_hmac_key_t){
207
2
    .magic = hmac_key_magic,
208
2
    .len = key_len,
209
2
    .params = md_to_hmac_params[type],
210
2
  };
211
2
  memmove(key->secret, secret, key_len);
212
2
  isc_mem_attach(mctx, &key->mctx);
213
214
2
  *keyp = key;
215
216
2
  return ISC_R_SUCCESS;
217
2
}
218
219
void
220
0
isc_hmac_key_destroy(isc_hmac_key_t **keyp) {
221
0
  isc_hmac_key_t *key;
222
223
0
  REQUIRE(keyp != NULL && *keyp != NULL);
224
0
  REQUIRE((*keyp)->magic == hmac_key_magic);
225
226
0
  key = *keyp;
227
0
  *keyp = NULL;
228
229
0
  key->magic = 0x00;
230
231
0
  isc_safe_memwipe(key->secret, key->len);
232
0
  isc_mem_putanddetach(&key->mctx, key,
233
0
           STRUCT_FLEX_SIZE(key, secret, key->len));
234
0
}
235
236
isc_region_t
237
4
isc_hmac_key_expose(isc_hmac_key_t *key) {
238
4
  REQUIRE(key != NULL && key->magic == hmac_key_magic);
239
240
4
  return (isc_region_t){ .base = key->secret, .length = key->len };
241
4
}
242
243
bool
244
0
isc_hmac_key_equal(isc_hmac_key_t *a, isc_hmac_key_t *b) {
245
0
  REQUIRE(a != NULL && a->magic == hmac_key_magic);
246
0
  REQUIRE(b != NULL && b->magic == hmac_key_magic);
247
248
0
  if (a->params != b->params) {
249
0
    return false;
250
0
  }
251
252
0
  if (a->len != b->len) {
253
0
    return false;
254
0
  }
255
256
0
  return isc_safe_memequal(a->secret, b->secret, a->len);
257
0
}
258
259
isc_hmac_t *
260
46
isc_hmac_new(void) {
261
46
  EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(evp_hmac);
262
46
  RUNTIME_CHECK(ctx != NULL);
263
46
  return ctx;
264
46
}
265
266
void
267
46
isc_hmac_free(isc_hmac_t *hmac) {
268
46
  EVP_MAC_CTX_free(hmac);
269
46
}
270
271
isc_result_t
272
46
isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key) {
273
46
  REQUIRE(key != NULL && key->magic == hmac_key_magic);
274
46
  REQUIRE(hmac != NULL);
275
276
46
  if (EVP_MAC_init(hmac, key->secret, key->len, key->params) != 1) {
277
0
    ERR_clear_error();
278
0
    return ISC_R_CRYPTOFAILURE;
279
0
  }
280
281
46
  return ISC_R_SUCCESS;
282
46
}
283
284
isc_result_t
285
311
isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) {
286
311
  REQUIRE(hmac != NULL);
287
288
311
  if (buf == NULL || len == 0) {
289
0
    return ISC_R_SUCCESS;
290
0
  }
291
292
311
  if (EVP_MAC_update(hmac, buf, len) != 1) {
293
0
    ERR_clear_error();
294
0
    return ISC_R_CRYPTOFAILURE;
295
0
  }
296
297
311
  return ISC_R_SUCCESS;
298
311
}
299
300
isc_result_t
301
46
isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out) {
302
46
  size_t len;
303
304
46
  REQUIRE(hmac != NULL);
305
306
46
  len = isc_buffer_availablelength(out);
307
46
  if (len < EVP_MAC_CTX_get_mac_size(hmac)) {
308
0
    return ISC_R_NOSPACE;
309
0
  }
310
311
46
  if (EVP_MAC_final(hmac, isc_buffer_used(out), &len, len) != 1) {
312
0
    ERR_clear_error();
313
0
    return ISC_R_CRYPTOFAILURE;
314
0
  }
315
316
46
  isc_buffer_add(out, len);
317
318
46
  return ISC_R_SUCCESS;
319
46
}
320
321
bool
322
22
isc_crypto_fips_mode(void) {
323
22
  return EVP_default_properties_is_fips_enabled(NULL) != 0;
324
22
}
325
326
isc_result_t
327
0
isc_crypto_fips_enable(void) {
328
0
  if (isc_crypto_fips_mode()) {
329
0
    return ISC_R_SUCCESS;
330
0
  }
331
332
0
  INSIST(fips == NULL);
333
0
  fips = OSSL_PROVIDER_load(NULL, "fips");
334
0
  if (fips == NULL) {
335
0
    return isc_ossl_wrap_logged_toresult(
336
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
337
0
      "OSSL_PROVIDER_load", ISC_R_CRYPTOFAILURE);
338
0
  }
339
340
0
  INSIST(base == NULL);
341
0
  base = OSSL_PROVIDER_load(NULL, "base");
342
0
  if (base == NULL) {
343
0
    OSSL_PROVIDER_unload(fips);
344
0
    return isc_ossl_wrap_logged_toresult(
345
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
346
0
      "OSS_PROVIDER_load", ISC_R_CRYPTOFAILURE);
347
0
  }
348
349
0
  if (EVP_default_properties_enable_fips(NULL, 1) == 0) {
350
0
    return isc_ossl_wrap_logged_toresult(
351
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
352
0
      "EVP_default_properties_enable_fips",
353
0
      ISC_R_CRYPTOFAILURE);
354
0
  }
355
356
0
  unregister_algorithms();
357
0
  register_algorithms();
358
359
0
  return ISC_R_SUCCESS;
360
0
}
361
362
/*
363
 * OPENSSL_cleanup() in OpenSSL 4 doesn't free the memory, which is not
364
 * compatible with BIND 9's memory leak detection code, that is why the memory
365
 * tracking has been disabled in this module, and this function is a no-op.
366
 * This can be cleaned up once OpenSSL 1.1.x support is removed.
367
 *
368
 * See https://github.com/openssl/openssl/pull/29721
369
 */
370
void
371
0
isc__crypto_setdestroycheck(bool check) {
372
0
  UNUSED(check);
373
0
}
374
375
void
376
22
isc__crypto_initialize(void) {
377
  /*
378
   * We call OPENSSL_cleanup() manually, in a correct order, thus disable
379
   * the automatic atexit() handler.
380
   */
381
22
  uint64_t opts = OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_NO_ATEXIT;
382
383
22
  RUNTIME_CHECK(OPENSSL_init_ssl(opts, NULL) == 1);
384
385
22
  register_algorithms();
386
387
#if defined(ENABLE_FIPS_MODE)
388
  if (isc_crypto_fips_enable() != ISC_R_SUCCESS) {
389
    ERR_clear_error();
390
    FATAL_ERROR("Failed to toggle FIPS mode but is "
391
          "required for this build");
392
  }
393
#endif
394
395
  /* Protect ourselves against unseeded PRNG */
396
22
  if (RAND_status() != 1) {
397
0
    isc_ossl_wrap_logged_toresult(
398
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
399
0
      "RAND_status", ISC_R_CRYPTOFAILURE);
400
0
    FATAL_ERROR("OpenSSL pseudorandom number generator "
401
0
          "cannot be initialized (see the `PRNG not "
402
0
          "seeded' message in the OpenSSL FAQ)");
403
0
  }
404
22
}
405
406
void
407
0
isc__crypto_shutdown(void) {
408
0
  unregister_algorithms();
409
410
0
  if (base != NULL) {
411
0
    OSSL_PROVIDER_unload(base);
412
0
  }
413
414
0
  if (fips != NULL) {
415
0
    OSSL_PROVIDER_unload(fips);
416
0
  }
417
418
0
  OPENSSL_cleanup();
419
0
}