Coverage Report

Created: 2026-02-26 06:45

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 isc_mem_t *isc__crypto_mctx = NULL;
48
49
static OSSL_PROVIDER *base = NULL, *fips = NULL;
50
51
static EVP_MAC *evp_hmac = NULL;
52
53
static OSSL_PARAM md_to_hmac_params[ISC_MD_MAX][2] = {
54
  [ISC_MD_UNKNOWN] = { OSSL_PARAM_END },
55
  [ISC_MD_MD5] = {
56
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("MD5"), sizeof("MD5") - 1),
57
    OSSL_PARAM_END,
58
  },
59
  [ISC_MD_SHA1] = {
60
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA1"), sizeof("SHA1") - 1),
61
    OSSL_PARAM_END,
62
  },
63
  [ISC_MD_SHA224] = {
64
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-224"), sizeof("SHA2-224") - 1),
65
    OSSL_PARAM_END,
66
  },
67
  [ISC_MD_SHA256] = {
68
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-256"), sizeof("SHA2-256") - 1),
69
    OSSL_PARAM_END,
70
  },
71
  [ISC_MD_SHA384] = {
72
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-384"), sizeof("SHA2-384") - 1),
73
    OSSL_PARAM_END,
74
  },
75
  [ISC_MD_SHA512] = {
76
    OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-512"), sizeof("SHA2-512") - 1),
77
    OSSL_PARAM_END,
78
  },
79
};
80
81
#define md_register_algorithm(alg)                                             \
82
132
  {                                                                      \
83
132
    REQUIRE(isc__crypto_md[ISC_MD_##alg] == NULL);                 \
84
132
    isc__crypto_md[ISC_MD_##alg] = EVP_MD_fetch(NULL, #alg, NULL); \
85
132
    if (isc__crypto_md[ISC_MD_##alg] == NULL) {                    \
86
0
      ERR_clear_error();                                     \
87
0
    }                                                              \
88
132
  }
89
90
static isc_result_t
91
22
register_algorithms(void) {
92
22
  if (!isc_crypto_fips_mode()) {
93
22
    md_register_algorithm(MD5);
94
22
  }
95
96
22
  md_register_algorithm(SHA1);
97
22
  md_register_algorithm(SHA224);
98
22
  md_register_algorithm(SHA256);
99
22
  md_register_algorithm(SHA384);
100
22
  md_register_algorithm(SHA512);
101
102
  /* We _must_ have HMAC */
103
22
  evp_hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
104
22
  if (evp_hmac == NULL) {
105
0
    ERR_clear_error();
106
0
    FATAL_ERROR("OpenSSL failed to find an HMAC implementation. "
107
0
          "Please make sure the default provider has an "
108
0
          "EVP_MAC-HMAC implementation");
109
0
  }
110
111
22
  return ISC_R_SUCCESS;
112
22
}
113
114
static void
115
0
unregister_algorithms(void) {
116
0
  for (size_t i = 0; i < ISC_MD_MAX; i++) {
117
0
    if (isc__crypto_md[i] != NULL) {
118
0
      EVP_MD_free(isc__crypto_md[i]);
119
0
      isc__crypto_md[i] = NULL;
120
0
    }
121
0
  }
122
123
0
  INSIST(evp_hmac != NULL);
124
0
  EVP_MAC_free(evp_hmac);
125
0
  evp_hmac = NULL;
126
0
}
127
128
#undef md_register_algorithm
129
130
/*
131
 * HMAC
132
 */
133
134
/*
135
 * Do not call EVP_Q_mac or HMAC (since it calls EVP_Q_mac internally)
136
 *
137
 * Each invocation of the EVP_Q_mac function causes an explicit fetch.
138
 */
139
isc_result_t
140
isc_hmac(isc_md_type_t type, const void *key, const size_t keylen,
141
   const unsigned char *buf, const size_t len, unsigned char *digest,
142
132
   unsigned int *digestlen) {
143
132
  EVP_MAC_CTX *ctx;
144
132
  size_t maclen;
145
146
132
  REQUIRE(type < ISC_MD_MAX);
147
148
132
  if (isc__crypto_md[type] == NULL) {
149
0
    return ISC_R_NOTIMPLEMENTED;
150
0
  }
151
152
132
  ctx = EVP_MAC_CTX_new(evp_hmac);
153
132
  RUNTIME_CHECK(ctx != NULL);
154
155
132
  if (EVP_MAC_init(ctx, key, keylen, md_to_hmac_params[type]) != 1) {
156
0
    goto fail;
157
0
  }
158
159
132
  if (EVP_MAC_update(ctx, buf, len) != 1) {
160
0
    goto fail;
161
0
  }
162
163
132
  maclen = *digestlen;
164
132
  if (EVP_MAC_final(ctx, digest, &maclen, maclen) != 1) {
165
0
    goto fail;
166
0
  }
167
168
132
  *digestlen = maclen;
169
170
132
  EVP_MAC_CTX_free(ctx);
171
132
  return ISC_R_SUCCESS;
172
173
0
fail:
174
0
  ERR_clear_error();
175
0
  EVP_MAC_CTX_free(ctx);
176
0
  return ISC_R_CRYPTOFAILURE;
177
132
}
178
179
/*
180
 * You do not need to process the key to fit the block size.
181
 *
182
 * https://github.com/openssl/openssl/blob/925e4fba1098036e8f8d22652cff6f64c5c7d571/crypto/hmac/hmac.c#L61-L80
183
 */
184
isc_result_t
185
isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len,
186
2
        isc_mem_t *mctx, isc_hmac_key_t **keyp) {
187
2
  isc_hmac_key_t *key;
188
2
  uint8_t digest[ISC_MAX_MD_SIZE];
189
2
  unsigned int digest_len = sizeof(digest);
190
2
  size_t key_len;
191
192
2
  REQUIRE(keyp != NULL && *keyp == NULL);
193
2
  REQUIRE(type < ISC_MD_MAX);
194
195
2
  if (isc__crypto_md[type] == NULL) {
196
0
    return ISC_R_NOTIMPLEMENTED;
197
0
  }
198
199
2
  if (len > (size_t)EVP_MD_block_size(isc__crypto_md[type])) {
200
0
    RETERR(isc_md(type, secret, len, digest, &digest_len));
201
0
    secret = digest;
202
0
    key_len = digest_len;
203
2
  } else {
204
2
    key_len = len;
205
2
  }
206
207
2
  key = isc_mem_get(mctx, STRUCT_FLEX_SIZE(key, secret, key_len));
208
2
  *key = (isc_hmac_key_t){
209
2
    .magic = hmac_key_magic,
210
2
    .len = key_len,
211
2
    .params = md_to_hmac_params[type],
212
2
  };
213
2
  memmove(key->secret, secret, key_len);
214
2
  isc_mem_attach(mctx, &key->mctx);
215
216
2
  *keyp = key;
217
218
2
  return ISC_R_SUCCESS;
219
2
}
220
221
void
222
0
isc_hmac_key_destroy(isc_hmac_key_t **keyp) {
223
0
  isc_hmac_key_t *key;
224
225
0
  REQUIRE(keyp != NULL && *keyp != NULL);
226
0
  REQUIRE((*keyp)->magic == hmac_key_magic);
227
228
0
  key = *keyp;
229
0
  *keyp = NULL;
230
231
0
  key->magic = 0x00;
232
233
0
  isc_safe_memwipe(key->secret, key->len);
234
0
  isc_mem_putanddetach(&key->mctx, key,
235
0
           STRUCT_FLEX_SIZE(key, secret, key->len));
236
0
}
237
238
isc_region_t
239
4
isc_hmac_key_expose(isc_hmac_key_t *key) {
240
4
  REQUIRE(key != NULL && key->magic == hmac_key_magic);
241
242
4
  return (isc_region_t){ .base = key->secret, .length = key->len };
243
4
}
244
245
bool
246
0
isc_hmac_key_equal(isc_hmac_key_t *a, isc_hmac_key_t *b) {
247
0
  REQUIRE(a != NULL && a->magic == hmac_key_magic);
248
0
  REQUIRE(b != NULL && b->magic == hmac_key_magic);
249
250
0
  if (a->params != b->params) {
251
0
    return false;
252
0
  }
253
254
0
  if (a->len != b->len) {
255
0
    return false;
256
0
  }
257
258
0
  return isc_safe_memequal(a->secret, b->secret, a->len);
259
0
}
260
261
isc_hmac_t *
262
42
isc_hmac_new(void) {
263
42
  EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(evp_hmac);
264
42
  RUNTIME_CHECK(ctx != NULL);
265
42
  return ctx;
266
42
}
267
268
void
269
42
isc_hmac_free(isc_hmac_t *hmac) {
270
42
  EVP_MAC_CTX_free(hmac);
271
42
}
272
273
isc_result_t
274
42
isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key) {
275
42
  REQUIRE(key != NULL && key->magic == hmac_key_magic);
276
42
  REQUIRE(hmac != NULL);
277
278
42
  if (EVP_MAC_init(hmac, key->secret, key->len, key->params) != 1) {
279
0
    ERR_clear_error();
280
0
    return ISC_R_CRYPTOFAILURE;
281
0
  }
282
283
42
  return ISC_R_SUCCESS;
284
42
}
285
286
isc_result_t
287
283
isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) {
288
283
  REQUIRE(hmac != NULL);
289
290
283
  if (buf == NULL || len == 0) {
291
0
    return ISC_R_SUCCESS;
292
0
  }
293
294
283
  if (EVP_MAC_update(hmac, buf, len) != 1) {
295
0
    ERR_clear_error();
296
0
    return ISC_R_CRYPTOFAILURE;
297
0
  }
298
299
283
  return ISC_R_SUCCESS;
300
283
}
301
302
isc_result_t
303
42
isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out) {
304
42
  size_t len;
305
306
42
  REQUIRE(hmac != NULL);
307
308
42
  len = isc_buffer_availablelength(out);
309
42
  if (len < EVP_MAC_CTX_get_mac_size(hmac)) {
310
0
    return ISC_R_NOSPACE;
311
0
  }
312
313
42
  if (EVP_MAC_final(hmac, isc_buffer_used(out), &len, len) != 1) {
314
0
    ERR_clear_error();
315
0
    return ISC_R_CRYPTOFAILURE;
316
0
  }
317
318
42
  isc_buffer_add(out, len);
319
320
42
  return ISC_R_SUCCESS;
321
42
}
322
323
#if ISC_MEM_TRACKLINES
324
/*
325
 * We use the internal isc__mem API here, so we can pass the file and line
326
 * arguments passed from OpenSSL >= 1.1.0 to our memory functions for better
327
 * tracking of the OpenSSL allocations.  Without this, we would always just see
328
 * isc__crypto_{malloc,realloc,free} in the tracking output, but with this in
329
 * place we get to see the places in the OpenSSL code where the allocations
330
 * happen.
331
 */
332
333
static void *
334
isc__crypto_malloc_ex(size_t size, const char *file, int line) {
335
  return isc__mem_allocate(isc__crypto_mctx, size, 0, __func__, file,
336
         (unsigned int)line);
337
}
338
339
static void *
340
isc__crypto_realloc_ex(void *ptr, size_t size, const char *file, int line) {
341
  return isc__mem_reallocate(isc__crypto_mctx, ptr, size, 0, __func__,
342
           file, (unsigned int)line);
343
}
344
345
static void
346
isc__crypto_free_ex(void *ptr, const char *file, int line) {
347
  if (ptr == NULL) {
348
    return;
349
  }
350
  if (isc__crypto_mctx != NULL) {
351
    isc__mem_free(isc__crypto_mctx, ptr, 0, __func__, file,
352
            (unsigned int)line);
353
  }
354
}
355
356
#else /* ISC_MEM_TRACKLINES */
357
358
static void *
359
193k
isc__crypto_malloc_ex(size_t size, const char *file, int line) {
360
193k
  UNUSED(file);
361
193k
  UNUSED(line);
362
193k
  return isc_mem_allocate(isc__crypto_mctx, size);
363
193k
}
364
365
static void *
366
4.15k
isc__crypto_realloc_ex(void *ptr, size_t size, const char *file, int line) {
367
4.15k
  UNUSED(file);
368
4.15k
  UNUSED(line);
369
4.15k
  return isc_mem_reallocate(isc__crypto_mctx, ptr, size);
370
4.15k
}
371
372
static void
373
100k
isc__crypto_free_ex(void *ptr, const char *file, int line) {
374
100k
  UNUSED(file);
375
100k
  UNUSED(line);
376
100k
  if (ptr == NULL) {
377
20.9k
    return;
378
20.9k
  }
379
79.7k
  if (isc__crypto_mctx != NULL) {
380
79.7k
    isc__mem_free(isc__crypto_mctx, ptr, 0);
381
79.7k
  }
382
79.7k
}
383
384
#endif /* ISC_MEM_TRACKLINES */
385
386
bool
387
22
isc_crypto_fips_mode(void) {
388
22
  return EVP_default_properties_is_fips_enabled(NULL) != 0;
389
22
}
390
391
isc_result_t
392
0
isc_crypto_fips_enable(void) {
393
0
  if (isc_crypto_fips_mode()) {
394
0
    return ISC_R_SUCCESS;
395
0
  }
396
397
0
  INSIST(fips == NULL);
398
0
  fips = OSSL_PROVIDER_load(NULL, "fips");
399
0
  if (fips == NULL) {
400
0
    return isc_ossl_wrap_logged_toresult(
401
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
402
0
      "OSSL_PROVIDER_load", ISC_R_CRYPTOFAILURE);
403
0
  }
404
405
0
  INSIST(base == NULL);
406
0
  base = OSSL_PROVIDER_load(NULL, "base");
407
0
  if (base == NULL) {
408
0
    OSSL_PROVIDER_unload(fips);
409
0
    return isc_ossl_wrap_logged_toresult(
410
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
411
0
      "OSS_PROVIDER_load", ISC_R_CRYPTOFAILURE);
412
0
  }
413
414
0
  if (EVP_default_properties_enable_fips(NULL, 1) == 0) {
415
0
    return isc_ossl_wrap_logged_toresult(
416
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
417
0
      "EVP_default_properties_enable_fips",
418
0
      ISC_R_CRYPTOFAILURE);
419
0
  }
420
421
0
  unregister_algorithms();
422
0
  register_algorithms();
423
424
0
  return ISC_R_SUCCESS;
425
0
}
426
427
void
428
0
isc__crypto_setdestroycheck(bool check) {
429
0
  isc_mem_setdestroycheck(isc__crypto_mctx, check);
430
0
}
431
432
void
433
22
isc__crypto_initialize(void) {
434
  /*
435
   * We call OPENSSL_cleanup() manually, in a correct order, thus disable
436
   * the automatic atexit() handler.
437
   */
438
22
  uint64_t opts = OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_NO_ATEXIT;
439
440
22
  isc_mem_create("OpenSSL", &isc__crypto_mctx);
441
22
  isc_mem_setdebugging(isc__crypto_mctx, 0);
442
22
  isc_mem_setdestroycheck(isc__crypto_mctx, false);
443
444
  /*
445
   * CRYPTO_set_mem_(_ex)_functions() returns 1 on success or 0 on
446
   * failure, which means OpenSSL already allocated some memory.  There's
447
   * nothing we can do about it.
448
   */
449
22
  (void)CRYPTO_set_mem_functions(isc__crypto_malloc_ex,
450
22
               isc__crypto_realloc_ex,
451
22
               isc__crypto_free_ex);
452
453
22
  RUNTIME_CHECK(OPENSSL_init_ssl(opts, NULL) == 1);
454
455
22
  register_algorithms();
456
457
#if defined(ENABLE_FIPS_MODE)
458
  if (isc_crypto_fips_enable() != ISC_R_SUCCESS) {
459
    ERR_clear_error();
460
    FATAL_ERROR("Failed to toggle FIPS mode but is "
461
          "required for this build");
462
  }
463
#endif
464
465
  /* Protect ourselves against unseeded PRNG */
466
22
  if (RAND_status() != 1) {
467
0
    isc_ossl_wrap_logged_toresult(
468
0
      ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
469
0
      "RAND_status", ISC_R_CRYPTOFAILURE);
470
0
    FATAL_ERROR("OpenSSL pseudorandom number generator "
471
0
          "cannot be initialized (see the `PRNG not "
472
0
          "seeded' message in the OpenSSL FAQ)");
473
0
  }
474
22
}
475
476
void
477
0
isc__crypto_shutdown(void) {
478
0
  unregister_algorithms();
479
480
0
  if (base != NULL) {
481
0
    OSSL_PROVIDER_unload(base);
482
0
  }
483
484
0
  if (fips != NULL) {
485
0
    OSSL_PROVIDER_unload(fips);
486
0
  }
487
488
0
  OPENSSL_cleanup();
489
490
0
  isc_mem_detach(&isc__crypto_mctx);
491
0
}