• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 17765900968

16 Sep 2025 12:33PM UTC coverage: 65.987% (-0.04%) from 66.029%
17765900968

Pull #16108

github

web-flow
Merge 4059c5fe8 into 2e297650d
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42424 of 93030 branches covered (45.6%)

Branch coverage included in aggregate %.

9 of 135 new or added lines in 6 files covered. (6.67%)

34 existing lines in 8 files now uncovered.

128910 of 166619 relevant lines covered (77.37%)

5500581.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.0
/pdns/opensslsigners.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22

23
#include "config.h"
24
#include "misc.hh"
25
#include <memory>
26
#include <openssl/crypto.h>
27
#include <openssl/ec.h>
28
#include <optional>
29
#include <openssl/obj_mac.h>
30
#ifdef HAVE_LIBCRYPTO_ECDSA
31
#include <openssl/ecdsa.h>
32
#endif
33
#if defined(HAVE_LIBCRYPTO_ED25519) || defined(HAVE_LIBCRYPTO_ED448)
34
#include <openssl/evp.h>
35
#endif
36
#include <openssl/bn.h>
37
#include <openssl/sha.h>
38
#include <openssl/rand.h>
39
#include <openssl/rsa.h>
40
#if OPENSSL_VERSION_MAJOR >= 3
41
#include <openssl/types.h>
42
#include <openssl/core_names.h>
43
#include <openssl/param_build.h>
44
#include <openssl/params.h>
45
#endif
46
#include <openssl/opensslv.h>
47
#include <openssl/err.h>
48
#include <openssl/pem.h>
49
#include "opensslsigners.hh"
50
#include "dnssecinfra.hh"
51
#include "dnsseckeeper.hh"
52

53
#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL)
54
/* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */
55

56
#include "lock.hh"
57
static std::vector<std::mutex> openssllocks;
58

59
extern "C"
60
{
61
  static void openssl_pthreads_locking_callback(int mode, int type, const char* file, int line)
62
  {
63
    if (mode & CRYPTO_LOCK) {
64
      openssllocks.at(type).lock();
65
    }
66
    else {
67
      openssllocks.at(type).unlock();
68
    }
69
  }
70

71
  static unsigned long openssl_pthreads_id_callback(void)
72
  {
73
    return (unsigned long)pthread_self();
74
  }
75
}
76

77
void openssl_thread_setup()
78
{
79
  openssllocks = std::vector<std::mutex>(CRYPTO_num_locks());
80
  CRYPTO_set_id_callback(&openssl_pthreads_id_callback);
81
  CRYPTO_set_locking_callback(&openssl_pthreads_locking_callback);
82
}
83

84
void openssl_thread_cleanup()
85
{
86
  CRYPTO_set_locking_callback(nullptr);
87
  openssllocks.clear();
88
}
89

90
#ifndef HAVE_RSA_GET0_KEY
91
/* those symbols are defined in LibreSSL 2.7.0+ */
92
/* compat helpers. These DO NOT do any of the checking that the libssl 1.1 functions do. */
93
static inline void RSA_get0_key(const RSA* rsakey, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d)
94
{
95
  *n = rsakey->n;
96
  *e = rsakey->e;
97
  *d = rsakey->d;
98
}
99

100
static inline int RSA_set0_key(RSA* rsakey, BIGNUM* n, BIGNUM* e, BIGNUM* d)
101
{
102
  if (n) {
103
    BN_clear_free(rsakey->n);
104
    rsakey->n = n;
105
  }
106
  if (e) {
107
    BN_clear_free(rsakey->e);
108
    rsakey->e = e;
109
  }
110
  if (d) {
111
    BN_clear_free(rsakey->d);
112
    rsakey->d = d;
113
  }
114
  return 1;
115
}
116

117
static inline void RSA_get0_factors(const RSA* rsakey, const BIGNUM** p, const BIGNUM** q)
118
{
119
  *p = rsakey->p;
120
  *q = rsakey->q;
121
}
122

123
static inline int RSA_set0_factors(RSA* rsakey, BIGNUM* p, BIGNUM* q)
124
{
125
  BN_clear_free(rsakey->p);
126
  rsakey->p = p;
127
  BN_clear_free(rsakey->q);
128
  rsakey->q = q;
129
  return 1;
130
}
131

132
static inline void RSA_get0_crt_params(const RSA* rsakey, const BIGNUM** dmp1, const BIGNUM** dmq1, const BIGNUM** iqmp)
133
{
134
  *dmp1 = rsakey->dmp1;
135
  *dmq1 = rsakey->dmq1;
136
  *iqmp = rsakey->iqmp;
137
}
138

139
static inline int RSA_set0_crt_params(RSA* rsakey, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp)
140
{
141
  BN_clear_free(rsakey->dmp1);
142
  rsakey->dmp1 = dmp1;
143
  BN_clear_free(rsakey->dmq1);
144
  rsakey->dmq1 = dmq1;
145
  BN_clear_free(rsakey->iqmp);
146
  rsakey->iqmp = iqmp;
147
  return 1;
148
}
149

150
#ifdef HAVE_LIBCRYPTO_ECDSA
151
static inline void ECDSA_SIG_get0(const ECDSA_SIG* signature, const BIGNUM** pr, const BIGNUM** ps)
152
{
153
  *pr = signature->r;
154
  *ps = signature->s;
155
}
156

157
static inline int ECDSA_SIG_set0(ECDSA_SIG* signature, BIGNUM* pr, BIGNUM* ps)
158
{
159
  BN_clear_free(signature->r);
160
  BN_clear_free(signature->s);
161
  signature->r = pr;
162
  signature->s = ps;
163
  return 1;
164
}
165
#endif /* HAVE_LIBCRYPTO_ECDSA */
166

167
#endif /* HAVE_RSA_GET0_KEY */
168

169
#else
170
void openssl_thread_setup() {}
352✔
171
void openssl_thread_cleanup() {}
×
172
#endif
173

174
/* seeding PRNG */
175
void openssl_seed()
176
{
6,304✔
177
  std::string entropy;
6,304✔
178
  entropy.reserve(1024);
6,304✔
179

180
  unsigned int r;
6,304✔
181
  for (int i = 0; i < 1024; i += 4) {
1,620,128✔
182
    r = dns_random_uint32();
1,613,824✔
183
    entropy.append((const char*)&r, 4);
1,613,824✔
184
  }
1,613,824✔
185

186
  RAND_seed((const unsigned char*)entropy.c_str(), 1024);
6,304✔
187
}
6,304✔
188

189
using BigNum = unique_ptr<BIGNUM, decltype(&BN_clear_free)>;
190

191
static auto mapToBN(const std::string& componentName, const std::map<std::string, std::string>& stormap, const std::string& key) -> BigNum
192
{
13,485✔
193
  const std::string& value = stormap.at(key);
13,485✔
194

195
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
196
  const auto* valueCStr = reinterpret_cast<const unsigned char*>(value.c_str());
13,485✔
197
  auto number = BigNum{BN_bin2bn(valueCStr, static_cast<int>(value.length()), nullptr), BN_clear_free};
13,485✔
198
  if (number == nullptr) {
13,485!
199
    throw pdns::OpenSSL::error(componentName, "Failed to parse key `" + key + "`");
×
200
  }
×
201

202
  return number;
13,485✔
203
}
13,485✔
204

205
class OpenSSLRSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
206
{
207
public:
208
  explicit OpenSSLRSADNSCryptoKeyEngine(unsigned int algo);
209

210
  [[nodiscard]] string getName() const override { return "OpenSSL RSA"; }
8,308✔
211
  [[nodiscard]] int getBits() const override;
212
  void create(unsigned int bits) override;
213

214
  /**
215
   * \brief Creates an RSA key engine from a PEM file.
216
   *
217
   * Receives an open file handle with PEM contents and creates an RSA key engine.
218
   *
219
   * \param[in] drc Key record contents to be populated.
220
   *
221
   * \param[in] inputFile An open file handle to a file containing RSA PEM contents.
222
   *
223
   * \param[in] filename Only used for providing filename information in error messages.
224
   *
225
   * \return An RSA key engine populated with the contents of the PEM file.
226
   */
227
  void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
228

229
  /**
230
   * \brief Writes this key's contents to a file.
231
   *
232
   * Receives an open file handle and writes this key's contents to the
233
   * file.
234
   *
235
   * \param[in] outputFile An open file handle for writing.
236
   *
237
   * \exception std::runtime_error In case of OpenSSL errors.
238
   */
239
  void convertToPEMFile(std::FILE& outputFile) const override;
240

241
  [[nodiscard]] storvector_t convertToISCVector() const override;
242

243
  // TODO Fred: hash() can probably be completely removed. See #12464.
244
  [[nodiscard]] std::string hash(const std::string& message) const override;
245
  [[nodiscard]] std::string sign(const std::string& message) const override;
246
  [[nodiscard]] bool verify(const std::string& message, const std::string& signature) const override;
247
  [[nodiscard]] std::string getPublicKeyString() const override;
248

249
  void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
250
  void fromPublicKeyString(const std::string& content) override;
251
  [[nodiscard]] bool checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const override;
252

253
  static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
254
  {
5,337✔
255
    return make_unique<OpenSSLRSADNSCryptoKeyEngine>(algorithm);
5,337✔
256
  }
5,337✔
257

258
private:
259
#if OPENSSL_VERSION_MAJOR >= 3
260
  [[nodiscard]] BigNum getKeyParamModulus() const;
261
  [[nodiscard]] BigNum getKeyParamPublicExponent() const;
262
  [[nodiscard]] BigNum getKeyParamPrivateExponent() const;
263
  [[nodiscard]] BigNum getKeyParamPrime1() const;
264
  [[nodiscard]] BigNum getKeyParamPrime2() const;
265
  [[nodiscard]] BigNum getKeyParamDmp1() const;
266
  [[nodiscard]] BigNum getKeyParamDmq1() const;
267
  [[nodiscard]] BigNum getKeyParamIqmp() const;
268

269
  using Params = std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)>;
270
  auto makeKeyParams(const BIGNUM* modulus, const BIGNUM* publicExponent, const BIGNUM* privateExponent, const BIGNUM* prime1, const BIGNUM* prime2, const BIGNUM* dmp1, const BIGNUM* dmq1, const BIGNUM* iqmp) const -> Params;
271
#endif
272

273
  // TODO Fred: hashSize(), hasher() and hashSizeToKind() can probably be completely
274
  // removed along with hash(). See #12464.
275
  [[nodiscard]] std::size_t hashSize() const;
276
  [[nodiscard]] const EVP_MD* hasher() const;
277
  static int hashSizeToKind(size_t hashSize);
278

279
#if OPENSSL_VERSION_MAJOR >= 3
280
  using KeyContext = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
281
  using Key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
282
  using MessageDigestContext = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>;
283
  using ParamsBuilder = std::unique_ptr<OSSL_PARAM_BLD, decltype(&OSSL_PARAM_BLD_free)>;
284
  using MessageDigest = std::unique_ptr<EVP_MD, decltype(&EVP_MD_free)>;
285
#else
286
  using Key = std::unique_ptr<RSA, decltype(&RSA_free)>;
287
#endif
288

289
  Key d_key;
290
};
291

292
OpenSSLRSADNSCryptoKeyEngine::OpenSSLRSADNSCryptoKeyEngine(unsigned int algo) :
293
  DNSCryptoKeyEngine(algo),
294
#if OPENSSL_VERSION_MAJOR >= 3
295
  d_key(Key(nullptr, EVP_PKEY_free))
296
#else
297
  d_key(Key(nullptr, RSA_free))
298
#endif
299
{
5,337✔
300
  int ret = RAND_status();
5,337✔
301
  if (ret != 1) {
5,337!
302
    throw runtime_error(getName() + " insufficient entropy");
×
303
  }
×
304
}
5,337✔
305

306
int OpenSSLRSADNSCryptoKeyEngine::getBits() const
307
{
164✔
308
#if OPENSSL_VERSION_MAJOR >= 3
164✔
309
  return EVP_PKEY_get_bits(d_key.get());
164✔
310
#else
311
  return RSA_size(d_key.get()) << 3;
312
#endif
313
}
164✔
314

315
void OpenSSLRSADNSCryptoKeyEngine::create(unsigned int bits)
316
{
585✔
317
  // When changing the bitsizes, also edit them in ::checkKey
318
  if ((d_algorithm == DNSSECKeeper::RSASHA1 || d_algorithm == DNSSECKeeper::RSASHA1NSEC3SHA1) && (bits < 512 || bits > 4096)) {
585!
319
    /* RFC3110 */
320
    throw runtime_error(getName() + " RSASHA1 key generation failed for invalid bits size " + std::to_string(bits));
×
321
  }
×
322
  if (d_algorithm == DNSSECKeeper::RSASHA256 && (bits < 512 || bits > 4096)) {
585!
323
    /* RFC5702 */
324
    throw runtime_error(getName() + " RSASHA256 key generation failed for invalid bits size " + std::to_string(bits));
×
325
  }
×
326
  if (d_algorithm == DNSSECKeeper::RSASHA512 && (bits < 1024 || bits > 4096)) {
585!
327
    /* RFC5702 */
328
    throw runtime_error(getName() + " RSASHA512 key generation failed for invalid bits size " + std::to_string(bits));
4✔
329
  }
4✔
330

331
  auto exponent = BigNum(BN_new(), BN_clear_free);
581✔
332
  if (!exponent) {
581!
333
    throw runtime_error(getName() + " key generation failed, unable to allocate e");
×
334
  }
×
335

336
  /* RSA_F4 is a public exponent value of 65537 */
337
  int res = BN_set_word(exponent.get(), RSA_F4);
581✔
338

339
  if (res == 0) {
581!
340
    throw runtime_error(getName() + " key generation failed while setting e");
×
341
  }
×
342

343
#if OPENSSL_VERSION_MAJOR >= 3
581✔
344
  auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
581✔
345
  if (ctx == nullptr) {
581!
346
    throw pdns::OpenSSL::error(getName(), "Could not initialize context");
×
347
  }
×
348

349
  if (EVP_PKEY_keygen_init(ctx.get()) != 1) {
581!
350
    throw pdns::OpenSSL::error(getName(), "Could not initialize keygen");
×
351
  }
×
352

353
  if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), (int)bits) <= 0) {
581!
354
    throw pdns::OpenSSL::error(getName(), "Could not set keygen bits to " + std::to_string(bits));
×
355
  }
×
356

357
  if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx.get(), exponent.get()) <= 0) {
581!
358
    throw pdns::OpenSSL::error(getName(), "Could not set keygen public exponent");
×
359
  }
×
360

361
  EVP_PKEY* key = nullptr;
581✔
362
  if (EVP_PKEY_generate(ctx.get(), &key) != 1) {
581!
363
    throw pdns::OpenSSL::error(getName(), "Could not generate key");
×
364
  }
×
365

366
  d_key.reset(key);
581✔
367
#else
368
  auto key = Key(RSA_new(), RSA_free);
369
  if (!key) {
370
    throw runtime_error(getName() + " allocation of key structure failed");
371
  }
372

373
  res = RSA_generate_key_ex(key.get(), bits, exponent.get(), nullptr);
374
  if (res == 0) {
375
    throw runtime_error(getName() + " key generation failed");
376
  }
377

378
  d_key = std::move(key);
379
#endif
380
}
581✔
381

382
void OpenSSLRSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, const std::optional<std::reference_wrapper<const std::string>> filename)
383
{
3✔
384
  drc.d_algorithm = d_algorithm;
3✔
385

386
#if OPENSSL_VERSION_MAJOR >= 3
3✔
387
  EVP_PKEY* key = nullptr;
3✔
388
  if (PEM_read_PrivateKey(&inputFile, &key, nullptr, nullptr) == nullptr) {
3!
389
    if (filename.has_value()) {
×
390
      throw pdns::OpenSSL::error(getName(), "Could not read private key from PEM file `" + filename->get() + "`");
×
391
    }
×
392

393
    throw pdns::OpenSSL::error(getName(), "Could not read private key from PEM contents");
×
394
  }
×
395

396
  d_key.reset(key);
3✔
397
#else
398
  d_key = Key(PEM_read_RSAPrivateKey(&inputFile, nullptr, nullptr, nullptr), &RSA_free);
399
  if (d_key == nullptr) {
400
    if (filename.has_value()) {
401
      throw runtime_error(getName() + ": Failed to read private key from PEM file `" + filename->get() + "`");
402
    }
403

404
    throw runtime_error(getName() + ": Failed to read private key from PEM contents");
405
  }
406
#endif
407
}
3✔
408

409
void OpenSSLRSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
410
{
6✔
411
#if OPENSSL_VERSION_MAJOR >= 3
6✔
412
  if (PEM_write_PrivateKey(&outputFile, d_key.get(), nullptr, nullptr, 0, nullptr, nullptr) == 0) {
6!
413
    throw pdns::OpenSSL::error(getName(), "Could not convert private key to PEM");
×
414
  }
×
415
#else
416
  auto ret = PEM_write_RSAPrivateKey(&outputFile, d_key.get(), nullptr, nullptr, 0, nullptr, nullptr);
417
  if (ret == 0) {
418
    throw runtime_error(getName() + ": Could not convert private key to PEM");
419
  }
420
#endif
421
}
6✔
422

423
#if OPENSSL_VERSION_MAJOR >= 3
424
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamModulus() const
425
{
1,439✔
426
  BIGNUM* modulus = nullptr;
1,439✔
427
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_N, &modulus) == 0) {
1,439!
428
    throw pdns::OpenSSL::error(getName(), "Could not get key's modulus (n) parameter");
×
429
  }
×
430
  return BigNum{modulus, BN_clear_free};
1,439✔
431
}
1,439✔
432

433
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPublicExponent() const
434
{
1,439✔
435
  BIGNUM* publicExponent = nullptr;
1,439✔
436
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_E, &publicExponent) == 0) {
1,439!
437
    throw pdns::OpenSSL::error(getName(), "Could not get key's public exponent (e) parameter");
×
438
  }
×
439
  return BigNum{publicExponent, BN_clear_free};
1,439✔
440
}
1,439✔
441

442
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrivateExponent() const
443
{
212✔
444
  BIGNUM* privateExponent = nullptr;
212✔
445
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_D, &privateExponent) == 0) {
212!
446
    throw pdns::OpenSSL::error(getName(), "Could not get key's private exponent (d) parameter");
×
447
  }
×
448
  return BigNum{privateExponent, BN_clear_free};
212✔
449
}
212✔
450

451
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrime1() const
452
{
212✔
453
  BIGNUM* prime1 = nullptr;
212✔
454
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_FACTOR1, &prime1) == 0) {
212!
455
    throw pdns::OpenSSL::error(getName(), "Could not get key's first prime (p) parameter");
×
456
  }
×
457
  return BigNum{prime1, BN_clear_free};
212✔
458
}
212✔
459

460
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrime2() const
461
{
212✔
462
  BIGNUM* prime2 = nullptr;
212✔
463
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_FACTOR2, &prime2) == 0) {
212!
464
    throw pdns::OpenSSL::error(getName(), "Could not get key's second prime (q) parameter");
×
465
  }
×
466
  return BigNum{prime2, BN_clear_free};
212✔
467
}
212✔
468

469
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamDmp1() const
470
{
212✔
471
  BIGNUM* dmp1 = nullptr;
212✔
472
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_EXPONENT1, &dmp1) == 0) {
212!
473
    throw pdns::OpenSSL::error(getName(), "Could not get key's first exponent parameter");
×
474
  }
×
475
  return BigNum{dmp1, BN_clear_free};
212✔
476
}
212✔
477

478
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamDmq1() const
479
{
212✔
480
  BIGNUM* dmq1 = nullptr;
212✔
481
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_EXPONENT2, &dmq1) == 0) {
212!
482
    throw pdns::OpenSSL::error(getName(), "Could not get key's second exponent parameter");
×
483
  }
×
484
  return BigNum{dmq1, BN_clear_free};
212✔
485
}
212✔
486

487
BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamIqmp() const
488
{
212✔
489
  BIGNUM* iqmp = nullptr;
212✔
490
  if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_COEFFICIENT1, &iqmp) == 0) {
212!
491
    throw pdns::OpenSSL::error(getName(), "Could not get key's first coefficient parameter");
×
492
  }
×
493
  return BigNum{iqmp, BN_clear_free};
212✔
494
}
212✔
495
#endif
496

497
#if OPENSSL_VERSION_MAJOR >= 3
498
auto OpenSSLRSADNSCryptoKeyEngine::makeKeyParams(const BIGNUM* modulus, const BIGNUM* publicExponent, const BIGNUM* privateExponent, const BIGNUM* prime1, const BIGNUM* prime2, const BIGNUM* dmp1, const BIGNUM* dmq1, const BIGNUM* iqmp) const -> Params
499
{
1,770✔
500
  auto params_build = ParamsBuilder(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free);
1,770✔
501
  if (params_build == nullptr) {
1,770!
502
    throw pdns::OpenSSL::error(getName(), "Could not create key's parameters builder");
×
503
  }
×
504

505
  if ((modulus != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_N, modulus) == 0) {
1,770!
506
    throw pdns::OpenSSL::error(getName(), "Could not create key's modulus parameter");
×
507
  }
×
508

509
  if ((publicExponent != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_E, publicExponent) == 0) {
1,770!
510
    throw pdns::OpenSSL::error(getName(), "Could not create key's public exponent parameter");
×
511
  }
×
512

513
  if ((privateExponent != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_D, privateExponent) == 0) {
1,770!
514
    throw pdns::OpenSSL::error(getName(), "Could not create key's private exponent parameter");
×
515
  }
×
516

517
  if ((prime1 != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_FACTOR1, prime1) == 0) {
1,770!
518
    throw pdns::OpenSSL::error(getName(), "Could not create key's first prime parameter");
×
519
  }
×
520

521
  if ((prime2 != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_FACTOR2, prime2) == 0) {
1,770!
522
    throw pdns::OpenSSL::error(getName(), "Could not create key's second prime parameter");
×
523
  }
×
524

525
  if ((dmp1 != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1) == 0) {
1,770!
526
    throw pdns::OpenSSL::error(getName(), "Could not create key's first exponent parameter");
×
527
  }
×
528

529
  if ((dmq1 != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1) == 0) {
1,770!
530
    throw pdns::OpenSSL::error(getName(), "Could not create key's second exponent parameter");
×
531
  }
×
532

533
  if ((iqmp != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp) == 0) {
1,770!
534
    throw pdns::OpenSSL::error(getName(), "Could not create key's first coefficient parameter");
×
535
  }
×
536

537
  auto params = Params(OSSL_PARAM_BLD_to_param(params_build.get()), OSSL_PARAM_free);
1,770✔
538
  if (params == nullptr) {
1,770!
539
    throw pdns::OpenSSL::error(getName(), "Could not create key's parameters");
×
540
  }
×
541

542
  return params;
1,770✔
543
}
1,770✔
544
#endif
545

546
DNSCryptoKeyEngine::storvector_t OpenSSLRSADNSCryptoKeyEngine::convertToISCVector() const
547
{
212✔
548
  storvector_t storvect;
212✔
549
  using outputs_t = vector<pair<string, const BIGNUM*>>;
212✔
550
  outputs_t outputs;
212✔
551

552
#if OPENSSL_VERSION_MAJOR >= 3
212✔
553
  // If any of those calls throw, we correctly free the BIGNUMs allocated before it.
554
  BigNum modulusPtr = getKeyParamModulus();
212✔
555
  BigNum publicExponentPtr = getKeyParamPublicExponent();
212✔
556
  BigNum privateExponentPtr = getKeyParamPrivateExponent();
212✔
557
  BigNum prime1Ptr = getKeyParamPrime1();
212✔
558
  BigNum prime2Ptr = getKeyParamPrime2();
212✔
559
  BigNum dmp1Ptr = getKeyParamDmp1();
212✔
560
  BigNum dmq1Ptr = getKeyParamDmq1();
212✔
561
  BigNum iqmpPtr = getKeyParamIqmp();
212✔
562

563
  // All the calls succeeded, we can take references to the BIGNUM pointers.
564
  BIGNUM* modulus = modulusPtr.get();
212✔
565
  BIGNUM* publicExponent = publicExponentPtr.get();
212✔
566
  BIGNUM* privateExponent = privateExponentPtr.get();
212✔
567
  BIGNUM* prime1 = prime1Ptr.get();
212✔
568
  BIGNUM* prime2 = prime2Ptr.get();
212✔
569
  BIGNUM* dmp1 = dmp1Ptr.get();
212✔
570
  BIGNUM* dmq1 = dmq1Ptr.get();
212✔
571
  BIGNUM* iqmp = iqmpPtr.get();
212✔
572
#else
573
  const BIGNUM* modulus = nullptr;
574
  const BIGNUM* publicExponent = nullptr;
575
  const BIGNUM* privateExponent = nullptr;
576
  const BIGNUM* prime1 = nullptr;
577
  const BIGNUM* prime2 = nullptr;
578
  const BIGNUM* dmp1 = nullptr;
579
  const BIGNUM* dmq1 = nullptr;
580
  const BIGNUM* iqmp = nullptr;
581
  RSA_get0_key(d_key.get(), &modulus, &publicExponent, &privateExponent);
582
  RSA_get0_factors(d_key.get(), &prime1, &prime2);
583
  RSA_get0_crt_params(d_key.get(), &dmp1, &dmq1, &iqmp);
584
#endif
585

586
  outputs.emplace_back("Modulus", modulus);
212✔
587
  outputs.emplace_back("PublicExponent", publicExponent);
212✔
588
  outputs.emplace_back("PrivateExponent", privateExponent);
212✔
589
  outputs.emplace_back("Prime1", prime1);
212✔
590
  outputs.emplace_back("Prime2", prime2);
212✔
591
  outputs.emplace_back("Exponent1", dmp1);
212✔
592
  outputs.emplace_back("Exponent2", dmq1);
212✔
593
  outputs.emplace_back("Coefficient", iqmp);
212✔
594

595
  string algorithm = std::to_string(d_algorithm);
212✔
596
  switch (d_algorithm) {
212✔
597
  case DNSSECKeeper::RSASHA1:
1✔
598
  case DNSSECKeeper::RSASHA1NSEC3SHA1:
2✔
599
    algorithm += " (RSASHA1)";
2✔
600
    break;
2✔
601
  case DNSSECKeeper::RSASHA256:
160✔
602
    algorithm += " (RSASHA256)";
160✔
603
    break;
160✔
604
  case DNSSECKeeper::RSASHA512:
50✔
605
    algorithm += " (RSASHA512)";
50✔
606
    break;
50✔
607
  default:
×
608
    algorithm += " (?)";
×
609
  }
212✔
610
  storvect.emplace_back("Algorithm", algorithm);
212✔
611

612
  for (const outputs_t::value_type& value : outputs) {
1,696✔
613
    std::string tmp;
1,696✔
614
    tmp.resize(BN_num_bytes(value.second));
1,696✔
615
    // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
616
    int len = BN_bn2bin(value.second, reinterpret_cast<unsigned char*>(tmp.data()));
1,696✔
617
    if (len >= 0) {
1,696!
618
      tmp.resize(len);
1,696✔
619
      storvect.emplace_back(value.first, tmp);
1,696✔
620
    }
1,696✔
621
  }
1,696✔
622

623
  return storvect;
212✔
624
}
212✔
625

626
std::size_t OpenSSLRSADNSCryptoKeyEngine::hashSize() const
627
{
×
628
  switch (d_algorithm) {
×
629
  case DNSSECKeeper::RSASHA1:
×
630
  case DNSSECKeeper::RSASHA1NSEC3SHA1:
×
631
    return SHA_DIGEST_LENGTH;
×
632
  case DNSSECKeeper::RSASHA256:
×
633
    return SHA256_DIGEST_LENGTH;
×
634
  case DNSSECKeeper::RSASHA512:
×
635
    return SHA512_DIGEST_LENGTH;
×
636
  default:
×
637
    throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm));
×
638
  }
×
639
}
×
640

641
const EVP_MD* OpenSSLRSADNSCryptoKeyEngine::hasher() const
642
{
2,385✔
643
  const EVP_MD* messageDigest = nullptr;
2,385✔
644

645
  switch (d_algorithm) {
2,385✔
646
  case DNSSECKeeper::RSASHA1:
378✔
647
  case DNSSECKeeper::RSASHA1NSEC3SHA1:
756✔
648
    messageDigest = EVP_sha1();
756✔
649
    break;
756✔
650
  case DNSSECKeeper::RSASHA256:
1,367✔
651
    messageDigest = EVP_sha256();
1,367✔
652
    break;
1,367✔
653
  case DNSSECKeeper::RSASHA512:
262✔
654
    messageDigest = EVP_sha512();
262✔
655
    break;
262✔
656
  default:
×
657
    throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm));
×
658
  }
2,385✔
659

660
  if (messageDigest == nullptr) {
2,384!
661
    throw std::runtime_error("Could not retrieve a SHA implementation of size " + std::to_string(hashSize()) + " from OpenSSL");
×
662
  }
×
663

664
  return messageDigest;
2,384✔
665
}
2,384✔
666

667
std::string OpenSSLRSADNSCryptoKeyEngine::hash(const std::string& message) const
668
{
3,363✔
669
  if (d_algorithm == DNSSECKeeper::RSASHA1 || d_algorithm == DNSSECKeeper::RSASHA1NSEC3SHA1) {
3,363!
670
    std::string l_hash{};
449✔
671
    l_hash.resize(SHA_DIGEST_LENGTH);
449✔
672
    // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
673
    SHA1(reinterpret_cast<unsigned char*>(const_cast<char*>(message.c_str())), message.length(), reinterpret_cast<unsigned char*>(l_hash.data()));
449✔
674
    return l_hash;
449✔
675
  }
449✔
676

677
  if (d_algorithm == DNSSECKeeper::RSASHA256) {
2,914!
678
    std::string l_hash{};
2,914✔
679
    l_hash.resize(SHA256_DIGEST_LENGTH);
2,914✔
680
    // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
681
    SHA256(reinterpret_cast<unsigned char*>(const_cast<char*>(message.c_str())), message.length(), reinterpret_cast<unsigned char*>(l_hash.data()));
2,914✔
682
    return l_hash;
2,914✔
683
  }
2,914✔
684

685
  if (d_algorithm == DNSSECKeeper::RSASHA512) {
×
686
    std::string l_hash{};
×
687
    l_hash.resize(SHA512_DIGEST_LENGTH);
×
688
    // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
689
    SHA512(reinterpret_cast<unsigned char*>(const_cast<char*>(message.c_str())), message.length(), reinterpret_cast<unsigned char*>(l_hash.data()));
×
690
    return l_hash;
×
691
  }
×
692

693
  throw runtime_error(getName() + " does not support hash operation for algorithm " + std::to_string(d_algorithm));
×
694
}
×
695

696
int OpenSSLRSADNSCryptoKeyEngine::hashSizeToKind(const size_t hashSize)
697
{
×
698
  switch (hashSize) {
×
699
  case SHA_DIGEST_LENGTH:
×
700
    return NID_sha1;
×
701
  case SHA256_DIGEST_LENGTH:
×
702
    return NID_sha256;
×
703
  case SHA384_DIGEST_LENGTH:
×
704
    return NID_sha384;
×
705
  case SHA512_DIGEST_LENGTH:
×
706
    return NID_sha512;
×
707
  default:
×
708
    throw runtime_error("OpenSSL RSA does not handle hash of size " + std::to_string(hashSize));
×
709
  }
×
710
}
×
711

712
std::string OpenSSLRSADNSCryptoKeyEngine::sign(const std::string& message) const
713
{
1,249✔
714
  std::string signature;
1,249✔
715

716
#if OPENSSL_VERSION_MAJOR >= 3
1,249✔
717
  auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1,249✔
718
  if (ctx == nullptr) {
1,249!
719
    throw pdns::OpenSSL::error(getName(), "Could not create context for signing");
×
720
  }
×
721

722
  if (EVP_DigestSignInit(ctx.get(), nullptr, hasher(), nullptr, d_key.get()) == 0) {
1,249!
723
    throw pdns::OpenSSL::error(getName(), "Could not initialize context for signing");
×
724
  }
×
725

726
  std::size_t signatureLen = 0;
1,249✔
727
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
728
  const auto* messageData = reinterpret_cast<const unsigned char*>(message.data());
1,249✔
729
  if (EVP_DigestSign(ctx.get(), nullptr, &signatureLen, messageData, message.size()) == 0) {
1,249!
730
    throw pdns::OpenSSL::error(getName(), "Could not get message signature length");
×
731
  }
×
732

733
  signature.resize(signatureLen);
1,249✔
734

735
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
736
  auto* signatureData = reinterpret_cast<unsigned char*>(signature.data());
1,249✔
737
  if (EVP_DigestSign(ctx.get(), signatureData, &signatureLen, messageData, message.size()) == 0) {
1,249!
738
    throw pdns::OpenSSL::error(getName(), "Could not sign message");
×
739
  }
×
740
#else
741
  unsigned int signatureLen = 0;
742
  string l_hash = this->hash(message);
743
  int hashKind = hashSizeToKind(l_hash.size());
744
  signature.resize(RSA_size(d_key.get()));
745

746
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
747
  int res = RSA_sign(hashKind, reinterpret_cast<unsigned char*>(&l_hash.at(0)), l_hash.length(), reinterpret_cast<unsigned char*>(&signature.at(0)), &signatureLen, d_key.get());
748
  if (res != 1) {
749
    throw runtime_error(getName() + " failed to generate signature");
750
  }
751

752
  signature.resize(signatureLen);
753
#endif
754

755
  return signature;
1,249✔
756
}
1,249✔
757

758
bool OpenSSLRSADNSCryptoKeyEngine::verify(const std::string& message, const std::string& signature) const
759
{
1,137✔
760
#if OPENSSL_VERSION_MAJOR >= 3
1,137✔
761
  auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1,137✔
762
  if (ctx == nullptr) {
1,137!
763
    throw pdns::OpenSSL::error(getName(), "Failed to create context for verifying signature");
×
764
  }
×
765

766
  if (EVP_DigestVerifyInit(ctx.get(), nullptr, hasher(), nullptr, d_key.get()) == 0) {
1,137!
767
    throw pdns::OpenSSL::error(getName(), "Failed to initialize context for verifying signature");
×
768
  }
×
769

770
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
771
  const int ret = EVP_DigestVerify(ctx.get(), reinterpret_cast<const unsigned char*>(signature.data()), signature.size(), reinterpret_cast<const unsigned char*>(message.data()), message.size());
1,137✔
772
  if (ret < 0) {
1,137!
773
    throw pdns::OpenSSL::error(getName(), "Failed to verify message signature");
×
774
  }
×
775

776
  return (ret == 1);
1,137✔
777
#else
778
  string l_hash = this->hash(message);
779
  int hashKind = hashSizeToKind(l_hash.size());
780

781
  int ret = RSA_verify(hashKind, (const unsigned char*)l_hash.c_str(), l_hash.length(), (unsigned char*)signature.c_str(), signature.length(), d_key.get());
782

783
  return (ret == 1);
784
#endif
785
}
1,137✔
786

787
std::string OpenSSLRSADNSCryptoKeyEngine::getPublicKeyString() const
788
{
1,227✔
789
#if OPENSSL_VERSION_MAJOR >= 3
1,227✔
790
  // If any of those calls throw, we correctly free the BIGNUMs allocated before it.
791
  BigNum modulusPtr = getKeyParamModulus();
1,227✔
792
  BigNum publicExponentPtr = getKeyParamPublicExponent();
1,227✔
793

794
  // All the calls succeeded, we can take references to the BIGNUM pointers.
795
  BIGNUM* modulus = modulusPtr.get();
1,227✔
796
  BIGNUM* publicExponent = publicExponentPtr.get();
1,227✔
797
#else
798
  const BIGNUM* modulus = nullptr;
799
  const BIGNUM* publicExponent = nullptr;
800
  const BIGNUM* privateExponent = nullptr;
801
  RSA_get0_key(d_key.get(), &modulus, &publicExponent, &privateExponent);
802
#endif
803

804
  string keystring;
1,227✔
805
  std::string tmp;
1,227✔
806
  tmp.resize(std::max(BN_num_bytes(publicExponent), BN_num_bytes(modulus)));
1,227✔
807

808
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
809
  int len = BN_bn2bin(publicExponent, reinterpret_cast<unsigned char*>(&tmp.at(0)));
1,227✔
810
  if (len < 255) {
1,227!
811
    keystring.assign(1, (char)(unsigned int)len);
1,227✔
812
  }
1,227✔
813
  else {
×
814
    keystring.assign(1, 0);
×
815
    uint16_t tempLen = len;
×
816
    tempLen = htons(tempLen);
×
817
    keystring.append((char*)&tempLen, 2);
×
818
  }
×
819
  keystring.append(&tmp.at(0), len);
1,227✔
820

821
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
822
  len = BN_bn2bin(modulus, reinterpret_cast<unsigned char*>(&tmp.at(0)));
1,227✔
823
  keystring.append(&tmp.at(0), len);
1,227✔
824

825
  return keystring;
1,227✔
826
}
1,227✔
827

828
void OpenSSLRSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
829
{
1,035✔
830
  auto modulus = mapToBN(getName(), stormap, "modulus");
1,035✔
831
  auto publicExponent = mapToBN(getName(), stormap, "publicexponent");
1,035✔
832
  auto privateExponent = mapToBN(getName(), stormap, "privateexponent");
1,035✔
833

834
  auto prime1 = mapToBN(getName(), stormap, "prime1");
1,035✔
835
  auto prime2 = mapToBN(getName(), stormap, "prime2");
1,035✔
836

837
  auto dmp1 = mapToBN(getName(), stormap, "exponent1");
1,035✔
838
  auto dmq1 = mapToBN(getName(), stormap, "exponent2");
1,035✔
839
  auto iqmp = mapToBN(getName(), stormap, "coefficient");
1,035✔
840

841
  pdns::checked_stoi_into(drc.d_algorithm, stormap["algorithm"]);
1,035✔
842

843
  if (drc.d_algorithm != d_algorithm) {
1,035!
844
    throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc.d_algorithm) + " to a " + std::to_string(d_algorithm) + " key");
×
845
  }
×
846

847
#if OPENSSL_VERSION_MAJOR >= 3
1,035✔
848
  auto params = makeKeyParams(modulus.get(), publicExponent.get(), privateExponent.get(), prime1.get(), prime2.get(), dmp1.get(), dmq1.get(), iqmp.get());
1,035✔
849

850
  auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
1,035✔
851
  if (ctx == nullptr) {
1,035!
852
    throw pdns::OpenSSL::error(getName(), "Could not create key context");
×
853
  }
×
854

855
  if (EVP_PKEY_fromdata_init(ctx.get()) <= 0) {
1,035!
856
    throw pdns::OpenSSL::error(getName(), "Could not initialize key context for loading data from ISC");
×
857
  }
×
858

859
  EVP_PKEY* key = nullptr;
1,035✔
860
  if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
1,035!
861
    throw pdns::OpenSSL::error(getName(), "Could not create key from parameters");
×
862
  }
×
863

864
  d_key.reset(key);
1,035✔
865
#else
866
  auto key = Key(RSA_new(), RSA_free);
867
  if (!key) {
868
    throw runtime_error(getName() + " allocation of key structure failed");
869
  }
870

871
  // Everything OK, we're releasing ownership since the RSA_* functions want it
872
  RSA_set0_key(key.get(), modulus.release(), publicExponent.release(), privateExponent.release());
873
  RSA_set0_factors(key.get(), prime1.release(), prime2.release());
874
  RSA_set0_crt_params(key.get(), dmp1.release(), dmq1.release(), iqmp.release());
875

876
  d_key = std::move(key);
877
#endif
878
}
1,035✔
879

880
bool OpenSSLRSADNSCryptoKeyEngine::checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const
881
{
47✔
882
  bool retval = true;
47✔
883
  // When changing the bitsizes, also edit them in ::create
884
  if ((d_algorithm == DNSSECKeeper::RSASHA1 || d_algorithm == DNSSECKeeper::RSASHA1NSEC3SHA1 || d_algorithm == DNSSECKeeper::RSASHA256) && (getBits() < 512 || getBits() > 4096)) {
47!
885
    retval = false;
×
886
    if (errorMessages.has_value()) {
×
887
      errorMessages->get().push_back("key is " + std::to_string(getBits()) + " bytes, should be between 512 and 4096");
×
888
    }
×
889
  }
×
890
  if (d_algorithm == DNSSECKeeper::RSASHA512 && (getBits() < 1024 || getBits() > 4096)) {
47!
891
    retval = false;
×
892
    if (errorMessages.has_value()) {
×
893
      errorMessages->get().push_back("key is " + std::to_string(getBits()) + " bytes, should be between 1024 and 4096");
×
894
    }
×
895
  }
×
896

897
#if OPENSSL_VERSION_MAJOR >= 3
47✔
898
  auto ctx = KeyContext(EVP_PKEY_CTX_new_from_pkey(nullptr, d_key.get(), nullptr), EVP_PKEY_CTX_free);
47✔
899
  if (ctx == nullptr) {
47!
900
    throw pdns::OpenSSL::error(getName(), "Cannot create context to check key");
×
901
  }
×
902

903
  if (EVP_PKEY_pairwise_check(ctx.get()) != 1) {
47!
904
#else
905
  if (RSA_check_key(d_key.get()) != 1) {
906
#endif
907
    retval = false;
×
908
    if (errorMessages.has_value()) {
×
909
      const auto* errmsg = ERR_error_string(ERR_get_error(), nullptr);
×
910
      if (errmsg == nullptr) {
×
911
        errmsg = "Unknown OpenSSL error";
×
912
      }
×
913
      errorMessages->get().emplace_back(errmsg);
×
914
    }
×
915
  }
×
916
  return retval;
47✔
917
}
47✔
918

919
void OpenSSLRSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& content)
920
{
735✔
921
  string publicExponent;
735✔
922
  string modulus;
735✔
923
  const size_t contentLen = content.length();
735✔
924

925
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
926
  const auto* raw = reinterpret_cast<const unsigned char*>(content.c_str());
735✔
927

928
  if (contentLen < 1) {
735!
929
    throw runtime_error(getName() + " invalid input size for the public key");
×
930
  }
×
931

932
  if (raw[0] != 0) {
735!
933
    const size_t exponentSize = raw[0];
735✔
934
    if (contentLen < (exponentSize + 2)) {
735!
935
      throw runtime_error(getName() + " invalid input size for the public key");
×
936
    }
×
937
    publicExponent = content.substr(1, exponentSize);
735✔
938
    modulus = content.substr(exponentSize + 1);
735✔
939
  }
735✔
940
  else {
×
941
    if (contentLen < 3) {
×
942
      throw runtime_error(getName() + " invalid input size for the public key");
×
943
    }
×
944
    const size_t exponentSize = raw[1] * 0xff + raw[2];
×
945
    if (contentLen < (exponentSize + 4)) {
×
946
      throw runtime_error(getName() + " invalid input size for the public key");
×
947
    }
×
948
    publicExponent = content.substr(3, exponentSize);
×
949
    modulus = content.substr(exponentSize + 3);
×
950
  }
×
951

952
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
953
  auto publicExponentBN = BigNum(BN_bin2bn(reinterpret_cast<unsigned char*>(const_cast<char*>(publicExponent.c_str())), static_cast<int>(publicExponent.length()), nullptr), BN_clear_free);
735✔
954
  if (!publicExponentBN) {
735!
955
    throw runtime_error(getName() + " error loading public exponent (e) value of public key");
×
956
  }
×
957

958
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
959
  auto modulusBN = BigNum(BN_bin2bn(reinterpret_cast<unsigned char*>(const_cast<char*>(modulus.c_str())), static_cast<int>(modulus.length()), nullptr), BN_clear_free);
735✔
960
  if (!modulusBN) {
735!
961
    throw runtime_error(getName() + " error loading modulus (n) value of public key");
×
962
  }
×
963

964
#if OPENSSL_VERSION_MAJOR >= 3
735✔
965
  auto params = makeKeyParams(modulusBN.get(), publicExponentBN.get(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
735✔
966

967
  auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
735✔
968
  if (ctx == nullptr) {
735!
969
    throw pdns::OpenSSL::error(getName(), "Cannot create context to load key from public key data");
×
970
  }
×
971

972
  if (EVP_PKEY_fromdata_init(ctx.get()) <= 0) {
735!
973
    throw pdns::OpenSSL::error(getName(), "Could not initialize key context for loading data to check key");
×
974
  }
×
975

976
  EVP_PKEY* key = nullptr;
735✔
977
  if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_PUBLIC_KEY, params.get()) <= 0) {
735!
978
    throw pdns::OpenSSL::error(getName(), "Could not create public key from parameters");
×
979
  }
×
980

981
  d_key.reset(key);
735✔
982
#else
983
  auto key = Key(RSA_new(), RSA_free);
984
  if (!key) {
985
    throw runtime_error(getName() + " allocation of key structure failed");
986
  }
987

988
  RSA_set0_key(key.get(), modulusBN.release(), publicExponentBN.release(), nullptr);
989
  d_key = std::move(key);
990
#endif
991
}
735✔
992

993
#ifdef HAVE_LIBCRYPTO_ECDSA
994
class OpenSSLECDSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
995
{
996
public:
997
  explicit OpenSSLECDSADNSCryptoKeyEngine(unsigned int algo);
998

999
  [[nodiscard]] string getName() const override { return "OpenSSL ECDSA"; }
5,393✔
1000
  [[nodiscard]] int getBits() const override;
1001

1002
  void create(unsigned int bits) override;
1003

1004
  /**
1005
   * \brief Creates an ECDSA key engine from a PEM file.
1006
   *
1007
   * Receives an open file handle with PEM contents and creates an ECDSA key engine.
1008
   *
1009
   * \param[in] drc Key record contents to be populated.
1010
   *
1011
   * \param[in] inputFile An open file handle to a file containing ECDSA PEM contents.
1012
   *
1013
   * \param[in] filename Only used for providing filename information in error messages.
1014
   *
1015
   * \return An ECDSA key engine populated with the contents of the PEM file.
1016
   */
1017
  void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
1018

1019
  /**
1020
   * \brief Writes this key's contents to a file.
1021
   *
1022
   * Receives an open file handle and writes this key's contents to the
1023
   * file.
1024
   *
1025
   * \param[in] outputFile An open file handle for writing.
1026
   *
1027
   * \exception std::runtime_error In case of OpenSSL errors.
1028
   */
1029
  void convertToPEMFile(std::FILE& outputFile) const override;
1030

1031
  [[nodiscard]] storvector_t convertToISCVector() const override;
1032
  [[nodiscard]] std::string hash(const std::string& message) const override;
1033
  [[nodiscard]] std::string sign(const std::string& message) const override;
1034
  [[nodiscard]] bool verify(const std::string& message, const std::string& signature) const override;
1035
  [[nodiscard]] std::string getPublicKeyString() const override;
1036
  void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
1037
  void fromPublicKeyString(const std::string& content) override;
1038
  [[nodiscard]] bool checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const override;
1039

1040
  // TODO Fred: hashSize() and hasher() can probably be completely removed along with
1041
  // hash(). See #12464.
1042
  [[nodiscard]] std::size_t hashSize() const;
1043
  [[nodiscard]] const EVP_MD* hasher() const;
1044

1045
  static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
1046
  {
11,971✔
1047
    return make_unique<OpenSSLECDSADNSCryptoKeyEngine>(algorithm);
11,971✔
1048
  }
11,971✔
1049

1050
private:
1051
#if OPENSSL_VERSION_MAJOR >= 3
1052
  using BigNumContext = std::unique_ptr<BN_CTX, decltype(&BN_CTX_free)>;
1053
  using ParamsBuilder = std::unique_ptr<OSSL_PARAM_BLD, decltype(&OSSL_PARAM_BLD_free)>;
1054
  using Params = std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)>;
1055
  auto makeKeyParams(const std::string& group_name, const BIGNUM* privateKey, const std::optional<std::string>& publicKey) const -> Params;
1056
  [[nodiscard]] auto getPrivateKey() const -> BigNum;
1057
#endif
1058

1059
#if OPENSSL_VERSION_MAJOR >= 3
1060
  using Key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
1061
  using MessageDigestContext = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>;
1062
#else
1063
  using Key = std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)>;
1064
#endif
1065

1066
  using KeyContext = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
1067
  using Group = std::unique_ptr<EC_GROUP, decltype(&EC_GROUP_free)>;
1068
  using Point = std::unique_ptr<EC_POINT, decltype(&EC_POINT_free)>;
1069
  using Signature = std::unique_ptr<ECDSA_SIG, decltype(&ECDSA_SIG_free)>;
1070

1071
  int d_len{0};
1072
  std::string d_group_name{};
1073
  Group d_group{nullptr, EC_GROUP_free};
1074

1075
#if OPENSSL_VERSION_MAJOR >= 3
1076
  Key d_eckey{Key(nullptr, EVP_PKEY_free)};
1077
#else
1078
  Key d_eckey{Key(nullptr, EC_KEY_free)};
1079
#endif
1080
};
1081

1082
int OpenSSLECDSADNSCryptoKeyEngine::getBits() const
1083
{
1,372✔
1084
  return d_len << 3;
1,372✔
1085
}
1,372✔
1086

1087
OpenSSLECDSADNSCryptoKeyEngine::OpenSSLECDSADNSCryptoKeyEngine(unsigned int algo) :
1088
  DNSCryptoKeyEngine(algo)
1089
#if OPENSSL_VERSION_MAJOR < 3
1090
  ,
1091
  d_eckey(Key(EC_KEY_new(), EC_KEY_free))
1092
#endif
1093
{
11,972✔
1094
  int ret = RAND_status();
11,972✔
1095
  if (ret != 1) {
11,972!
1096
    throw runtime_error(getName() + " insufficient entropy");
×
1097
  }
×
1098

1099
#if OPENSSL_VERSION_MAJOR < 3
1100
  if (!d_eckey) {
1101
    throw runtime_error(getName() + " allocation of key structure failed");
1102
  }
1103
#endif
1104

1105
  int d_id{0};
11,972✔
1106

1107
  if (d_algorithm == 13) {
11,972✔
1108
    d_group_name = "P-256";
11,562✔
1109
    d_len = 32;
11,562✔
1110
    d_id = NID_X9_62_prime256v1;
11,562✔
1111
  }
11,562✔
1112
  else if (d_algorithm == 14) {
410!
1113
    d_group_name = "P-384";
410✔
1114
    d_len = 48;
410✔
1115
    d_id = NID_secp384r1;
410✔
1116
  }
410✔
1117
  else {
×
1118
    throw runtime_error(getName() + " unknown algorithm " + std::to_string(d_algorithm));
×
1119
  }
×
1120

1121
  d_group = Group(EC_GROUP_new_by_curve_name(d_id), EC_GROUP_free);
11,972✔
1122
  if (d_group == nullptr) {
11,972!
1123
    throw pdns::OpenSSL::error(getName(), std::string() + "Failed to create EC group `" + d_group_name + "` to export public key");
×
1124
  }
×
1125

1126
#if OPENSSL_VERSION_MAJOR < 3
1127
  ret = EC_KEY_set_group(d_eckey.get(), d_group.get());
1128
  if (ret != 1) {
1129
    throw runtime_error(getName() + " setting key group failed");
1130
  }
1131
#endif
1132
}
11,972✔
1133

1134
void OpenSSLECDSADNSCryptoKeyEngine::create(unsigned int bits)
1135
{
1,627✔
1136
  if (bits >> 3 != static_cast<unsigned int>(d_len)) {
1,627!
1137
    throw runtime_error(getName() + " unknown key length of " + std::to_string(bits) + " bits requested");
×
1138
  }
×
1139

1140
#if OPENSSL_VERSION_MAJOR >= 3
1,627✔
1141
  // NOLINTNEXTLINE(*-vararg): Using OpenSSL C APIs.
1142
  EVP_PKEY* key = EVP_PKEY_Q_keygen(nullptr, nullptr, "EC", d_group_name.c_str());
1,627✔
1143
  if (key == nullptr) {
1,627!
1144
    throw pdns::OpenSSL::error(getName(), "Failed to generate key");
×
1145
  }
×
1146

1147
  d_eckey.reset(key);
1,627✔
1148
#else
1149
  int res = EC_KEY_generate_key(d_eckey.get());
1150
  if (res == 0) {
1151
    throw runtime_error(getName() + " key generation failed");
1152
  }
1153

1154
  EC_KEY_set_asn1_flag(d_eckey.get(), OPENSSL_EC_NAMED_CURVE);
1155
#endif
1156
}
1,627✔
1157

1158
void OpenSSLECDSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename)
1159
{
3✔
1160
  drc.d_algorithm = d_algorithm;
3✔
1161

1162
#if OPENSSL_VERSION_MAJOR >= 3
3✔
1163
  EVP_PKEY* key = nullptr;
3✔
1164
  if (PEM_read_PrivateKey(&inputFile, &key, nullptr, nullptr) == nullptr) {
3!
1165
    if (filename.has_value()) {
×
1166
      throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM file `" + filename->get() + "`");
×
1167
    }
×
1168

1169
    throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM contents");
×
1170
  }
×
1171

1172
  d_eckey.reset(key);
3✔
1173
#else
1174
  d_eckey = Key(PEM_read_ECPrivateKey(&inputFile, nullptr, nullptr, nullptr), &EC_KEY_free);
1175
  if (d_eckey == nullptr) {
1176
    if (filename.has_value()) {
1177
      throw runtime_error(getName() + ": Failed to read private key from PEM file `" + filename->get() + "`");
1178
    }
1179

1180
    throw runtime_error(getName() + ": Failed to read private key from PEM contents");
1181
  }
1182

1183
  int ret = EC_KEY_set_group(d_eckey.get(), d_group.get());
1184
  if (ret != 1) {
1185
    throw runtime_error(getName() + " setting key group failed");
1186
  }
1187

1188
  const BIGNUM* privateKeyBN = EC_KEY_get0_private_key(d_eckey.get());
1189

1190
  auto pub_key = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
1191
  if (!pub_key) {
1192
    throw runtime_error(getName() + " allocation of public key point failed");
1193
  }
1194

1195
  ret = EC_POINT_mul(d_group.get(), pub_key.get(), privateKeyBN, nullptr, nullptr, nullptr);
1196
  if (ret != 1) {
1197
    throw runtime_error(getName() + " computing public key from private failed");
1198
  }
1199

1200
  ret = EC_KEY_set_public_key(d_eckey.get(), pub_key.get());
1201
  if (ret != 1) {
1202
    ERR_print_errors_fp(stderr);
1203
    throw runtime_error(getName() + " setting public key failed");
1204
  }
1205

1206
  EC_KEY_set_asn1_flag(d_eckey.get(), OPENSSL_EC_NAMED_CURVE);
1207
#endif
1208
}
3✔
1209

1210
void OpenSSLECDSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
1211
{
6✔
1212
#if OPENSSL_VERSION_MAJOR >= 3
6✔
1213
  if (PEM_write_PrivateKey(&outputFile, d_eckey.get(), nullptr, nullptr, 0, nullptr, nullptr) == 0) {
6!
1214
    throw pdns::OpenSSL::error(getName(), "Failed to convert private key to PEM");
×
1215
  }
×
1216
#else
1217
  auto ret = PEM_write_ECPrivateKey(&outputFile, d_eckey.get(), nullptr, nullptr, 0, nullptr, nullptr);
1218
  if (ret == 0) {
1219
    throw runtime_error(getName() + ": Could not convert private key to PEM");
1220
  }
1221
#endif
1222
}
6✔
1223

1224
#if OPENSSL_VERSION_MAJOR >= 3
1225
auto OpenSSLECDSADNSCryptoKeyEngine::getPrivateKey() const -> BigNum
1226
{
829✔
1227
  BIGNUM* privateKey = nullptr;
829✔
1228
  if (EVP_PKEY_get_bn_param(d_eckey.get(), OSSL_PKEY_PARAM_PRIV_KEY, &privateKey) == 0) {
829!
1229
    throw pdns::OpenSSL::error(getName(), "Could not get private key parameter");
×
1230
  }
×
1231
  return BigNum{privateKey, BN_clear_free};
829✔
1232
}
829✔
1233
#endif
1234

1235
DNSCryptoKeyEngine::storvector_t OpenSSLECDSADNSCryptoKeyEngine::convertToISCVector() const
1236
{
829✔
1237
  storvector_t storvect;
829✔
1238
  string algorithm;
829✔
1239

1240
  if (d_algorithm == 13) {
829✔
1241
    algorithm = "13 (ECDSAP256SHA256)";
791✔
1242
  }
791✔
1243
  else if (d_algorithm == 14) {
38!
1244
    algorithm = "14 (ECDSAP384SHA384)";
38✔
1245
  }
38✔
1246
  else {
×
1247
    algorithm = " ? (?)";
×
1248
  }
×
1249

1250
  storvect.emplace_back("Algorithm", algorithm);
829✔
1251

1252
#if OPENSSL_VERSION_MAJOR >= 3
829✔
1253
  auto privateKeyBN = getPrivateKey();
829✔
1254

1255
  std::string privateKey;
829✔
1256
  privateKey.resize(BN_num_bytes(privateKeyBN.get()));
829✔
1257
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1258
  int len = BN_bn2bin(privateKeyBN.get(), reinterpret_cast<unsigned char*>(privateKey.data()));
829✔
1259
  if (len >= 0) {
829!
1260
    privateKey.resize(len);
829✔
1261

1262
    std::string prefix;
829✔
1263
    if (d_len - len != 0) {
829!
UNCOV
1264
      prefix.append(d_len - len, 0x00);
×
UNCOV
1265
    }
×
1266

1267
    storvect.emplace_back("PrivateKey", prefix + privateKey);
829✔
1268
  }
829✔
1269
#else
1270
  const BIGNUM* key = EC_KEY_get0_private_key(d_eckey.get());
1271
  if (key == nullptr) {
1272
    throw runtime_error(getName() + " private key not set");
1273
  }
1274

1275
  std::string tmp;
1276
  tmp.resize(BN_num_bytes(key));
1277
  int len = BN_bn2bin(key, reinterpret_cast<unsigned char*>(&tmp.at(0)));
1278

1279
  string prefix;
1280
  if (d_len - len) {
1281
    prefix.append(d_len - len, 0x00);
1282
  }
1283

1284
  storvect.emplace_back("PrivateKey", prefix + tmp);
1285
#endif
1286

1287
  return storvect;
829✔
1288
}
829✔
1289

1290
std::string OpenSSLECDSADNSCryptoKeyEngine::hash(const std::string& message) const
1291
{
197✔
1292
  if (getBits() == 256) {
197!
1293
    std::string l_hash{};
×
1294
    l_hash.resize(SHA256_DIGEST_LENGTH);
×
1295
    // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1296
    SHA256(reinterpret_cast<unsigned char*>(const_cast<char*>(message.c_str())), message.length(), reinterpret_cast<unsigned char*>(l_hash.data()));
×
1297
    return l_hash;
×
1298
  }
×
1299

1300
  if (getBits() == 384) {
197!
1301
    std::string l_hash{};
197✔
1302
    l_hash.resize(SHA384_DIGEST_LENGTH);
197✔
1303
    // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1304
    SHA384(reinterpret_cast<unsigned char*>(const_cast<char*>(message.c_str())), message.length(), reinterpret_cast<unsigned char*>(l_hash.data()));
197✔
1305
    return l_hash;
197✔
1306
  }
197✔
1307

1308
  throw runtime_error(getName() + " does not support a hash size of " + std::to_string(getBits()) + " bits");
×
1309
}
197✔
1310

1311
const EVP_MD* OpenSSLECDSADNSCryptoKeyEngine::hasher() const
1312
{
701,030✔
1313
  const EVP_MD* messageDigest = nullptr;
701,030✔
1314

1315
  switch (d_algorithm) {
701,030✔
1316
  case DNSSECKeeper::ECDSA256:
700,361✔
1317
    messageDigest = EVP_sha256();
700,361✔
1318
    break;
700,361✔
1319
  case DNSSECKeeper::ECDSA384:
670✔
1320
    messageDigest = EVP_sha384();
670✔
1321
    break;
670✔
1322
  default:
×
1323
    throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm));
×
1324
  }
701,030✔
1325

1326
  if (messageDigest == nullptr) {
700,828!
1327
    throw std::runtime_error("Could not retrieve a SHA implementation of size " + std::to_string(hashSize()) + " from OpenSSL");
×
1328
  }
×
1329

1330
  return messageDigest;
700,828✔
1331
}
700,828✔
1332

1333
std::size_t OpenSSLECDSADNSCryptoKeyEngine::hashSize() const
1334
{
×
1335
  switch (d_algorithm) {
×
1336
  case DNSSECKeeper::ECDSA256:
×
1337
    return SHA256_DIGEST_LENGTH;
×
1338
  case DNSSECKeeper::ECDSA384:
×
1339
    return SHA384_DIGEST_LENGTH;
×
1340
  default:
×
1341
    throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm));
×
1342
  }
×
1343
}
×
1344

1345
std::string OpenSSLECDSADNSCryptoKeyEngine::sign(const std::string& message) const
1346
{
696,780✔
1347
#if OPENSSL_VERSION_MAJOR >= 3
696,780✔
1348
  auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
696,780✔
1349
  if (ctx == nullptr) {
696,780!
1350
    throw pdns::OpenSSL::error(getName(), "Could not create context for signing");
×
1351
  }
×
1352

1353
  if (EVP_DigestSignInit(ctx.get(), nullptr, hasher(), nullptr, d_eckey.get()) == 0) {
696,780!
1354
    throw pdns::OpenSSL::error(getName(), "Could not initialize context for signing");
×
1355
  }
×
1356

1357
  std::size_t signatureLen = 0;
696,780✔
1358

1359
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1360
  const auto* messageData = reinterpret_cast<const unsigned char*>(message.data());
696,780✔
1361
  if (EVP_DigestSign(ctx.get(), nullptr, &signatureLen, messageData, message.size()) == 0) {
696,780!
1362
    throw pdns::OpenSSL::error(getName(), "Could not get message signature length");
×
1363
  }
×
1364

1365
  std::string signatureBuffer;
696,780✔
1366
  signatureBuffer.resize(signatureLen);
696,780✔
1367

1368
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1369
  auto* signatureData = reinterpret_cast<unsigned char*>(signatureBuffer.data());
696,780✔
1370
  if (EVP_DigestSign(ctx.get(), signatureData, &signatureLen, messageData, message.size()) == 0) {
696,780!
1371
    throw pdns::OpenSSL::error(getName(), "Could not sign message");
×
1372
  }
×
1373

1374
  signatureBuffer.resize(signatureLen);
696,780✔
1375

1376
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1377
  auto signature = Signature(d2i_ECDSA_SIG(nullptr, const_cast<const unsigned char**>(&signatureData), (long)signatureLen), ECDSA_SIG_free);
696,780✔
1378
  if (signature == nullptr) {
696,780!
1379
    throw pdns::OpenSSL::error(getName(), "Failed to convert DER signature to internal structure");
×
1380
  }
×
1381
#else
1382
  string l_hash = this->hash(message);
1383

1384
  auto signature = Signature(ECDSA_do_sign((unsigned char*)l_hash.c_str(), l_hash.length(), d_eckey.get()), ECDSA_SIG_free);
1385
  if (!signature) {
1386
    throw runtime_error(getName() + " failed to generate signature");
1387
  }
1388
#endif
1389

1390
  string ret;
696,780✔
1391
  std::string tmp;
696,780✔
1392
  tmp.resize(d_len);
696,780✔
1393

1394
  const BIGNUM* prComponent = nullptr;
696,780✔
1395
  const BIGNUM* psComponent = nullptr;
696,780✔
1396
  ECDSA_SIG_get0(signature.get(), &prComponent, &psComponent);
696,780✔
1397
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1398
  int len = BN_bn2bin(prComponent, reinterpret_cast<unsigned char*>(&tmp.at(0)));
696,780✔
1399
  if ((d_len - len) != 0) {
696,780✔
1400
    ret.append(d_len - len, 0x00);
2,796✔
1401
  }
2,796✔
1402
  ret.append(&tmp.at(0), len);
696,780✔
1403

1404
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1405
  len = BN_bn2bin(psComponent, reinterpret_cast<unsigned char*>(&tmp.at(0)));
696,780✔
1406
  if ((d_len - len) != 0) {
696,780✔
1407
    ret.append(d_len - len, 0x00);
2,711✔
1408
  }
2,711✔
1409
  ret.append(&tmp.at(0), len);
696,780✔
1410

1411
  return ret;
696,780✔
1412
}
696,780✔
1413

1414
bool OpenSSLECDSADNSCryptoKeyEngine::verify(const std::string& message, const std::string& signature) const
1415
{
5,341✔
1416
  if (signature.length() != (static_cast<unsigned long>(d_len) * 2)) {
5,341!
1417
    throw runtime_error(getName() + " invalid signature size " + std::to_string(signature.length()));
×
1418
  }
×
1419

1420
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1421
  auto* signatureCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(signature.c_str()));
5,341✔
1422
  auto rComponent = BigNum(BN_bin2bn(signatureCStr, d_len, nullptr), BN_free);
5,341✔
1423
  auto sComponent = BigNum(BN_bin2bn(signatureCStr + d_len, d_len, nullptr), BN_free);
5,341✔
1424
  if (!rComponent || !sComponent) {
5,342!
1425
    throw runtime_error(getName() + " invalid signature");
×
1426
  }
×
1427

1428
  auto sig = Signature(ECDSA_SIG_new(), ECDSA_SIG_free);
5,341✔
1429
  if (!sig) {
5,341!
1430
    throw runtime_error(getName() + " allocation of signature structure failed");
×
1431
  }
×
1432
  ECDSA_SIG_set0(sig.get(), rComponent.release(), sComponent.release());
5,341✔
1433

1434
#if OPENSSL_VERSION_MAJOR >= 3
5,341✔
1435
  unsigned char* derBufferPointer = nullptr;
5,341✔
1436
  const int derBufferSize = i2d_ECDSA_SIG(sig.get(), &derBufferPointer);
5,341✔
1437
  if (derBufferSize < 0) {
5,341!
1438
    throw pdns::OpenSSL::error(getName(), "Failed to convert signature to DER");
×
1439
  }
×
1440
  // Because OPENSSL_free() is a macro.
1441
  auto derBuffer = unique_ptr<unsigned char, void (*)(unsigned char*)>{derBufferPointer, [](auto* buffer) { OPENSSL_free(buffer); }};
5,342✔
1442

1443
  auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
5,341✔
1444
  if (ctx == nullptr) {
5,341!
1445
    throw pdns::OpenSSL::error(getName(), "Could not create message digest context for signing");
×
1446
  }
×
1447

1448
  if (EVP_DigestVerifyInit(ctx.get(), nullptr, hasher(), nullptr, d_eckey.get()) == 0) {
5,341!
1449
    throw pdns::OpenSSL::error(getName(), "Could not initialize context for verifying signature");
×
1450
  }
×
1451

1452
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1453
  const auto ret = EVP_DigestVerify(ctx.get(), derBuffer.get(), derBufferSize, reinterpret_cast<const unsigned char*>(message.data()), message.size());
5,341✔
1454
  if (ret < 0) {
5,341!
1455
    throw pdns::OpenSSL::error(getName(), "Could not verify message signature");
×
1456
  }
×
1457

1458
  return (ret == 1);
5,341✔
1459
#else
1460
  string l_hash = this->hash(message);
1461

1462
  int ret = ECDSA_do_verify((unsigned char*)l_hash.c_str(), l_hash.length(), sig.get(), d_eckey.get());
1463
  if (ret == -1) {
1464
    throw runtime_error(getName() + " verify error");
1465
  }
1466

1467
  return (ret == 1);
1468
#endif
1469
}
5,341✔
1470

1471
std::string OpenSSLECDSADNSCryptoKeyEngine::getPublicKeyString() const
1472
{
6,639✔
1473
#if OPENSSL_VERSION_MAJOR >= 3
6,639✔
1474
  size_t bufsize = 0;
6,639✔
1475
  if (EVP_PKEY_get_octet_string_param(d_eckey.get(), OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &bufsize) == 0) {
6,639!
1476
    throw pdns::OpenSSL::error(getName(), "Failed to get public key buffer size");
×
1477
  }
×
1478

1479
  std::string publicKey{};
6,639✔
1480
  publicKey.resize(bufsize);
6,639✔
1481

1482
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1483
  auto* publicKeyCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(publicKey.c_str()));
6,639✔
1484
  if (EVP_PKEY_get_octet_string_param(d_eckey.get(), OSSL_PKEY_PARAM_PUB_KEY, publicKeyCStr, bufsize, &bufsize) == 0) {
6,639!
1485
    throw pdns::OpenSSL::error(getName(), "Failed to get public key");
×
1486
  }
×
1487

1488
  publicKey.resize(bufsize);
6,639✔
1489

1490
  auto publicKeyECPoint = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
6,639✔
1491
  if (publicKeyECPoint == nullptr) {
6,639!
1492
    throw pdns::OpenSSL::error(getName(), "Failed to create public key point for export");
×
1493
  }
×
1494

1495
  auto ctx = BigNumContext(BN_CTX_new(), BN_CTX_free);
6,639✔
1496

1497
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1498
  publicKeyCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(publicKey.c_str()));
6,639✔
1499
  if (EC_POINT_oct2point(d_group.get(), publicKeyECPoint.get(), publicKeyCStr, publicKey.length(), ctx.get()) == 0) {
6,639!
1500
    throw pdns::OpenSSL::error(getName(), "Failed to export public key to point");
×
1501
  }
×
1502

1503
  std::string publicKeyUncompressed{};
6,639✔
1504
  bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
6,639✔
1505
  if (bufsize == 0) {
6,639!
1506
    throw pdns::OpenSSL::error(getName(), "Failed to get public key binary buffer size");
×
1507
  }
×
1508
  publicKeyUncompressed.resize(bufsize);
6,639✔
1509

1510
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1511
  auto* publicKeyUncompressedCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(publicKeyUncompressed.c_str()));
6,639✔
1512
  bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_UNCOMPRESSED, publicKeyUncompressedCStr, publicKeyUncompressed.length(), nullptr);
6,639✔
1513
  if (bufsize == 0) {
6,639!
1514
    throw pdns::OpenSSL::error(getName(), "Failed to convert public key to oct");
×
1515
  }
×
1516

1517
  /* We skip the first byte as the other backends use raw field elements, as opposed to
1518
   * the format described in SEC1: "2.3.3 Elliptic-Curve-Point-to-Octet-String
1519
   * Conversion" */
1520
  publicKeyUncompressed.erase(0, 1);
6,639✔
1521

1522
  return publicKeyUncompressed;
6,639✔
1523
#else
1524
  std::string binaryPoint;
1525
  binaryPoint.resize((d_len * 2) + 1);
1526

1527
  int ret = EC_POINT_point2oct(d_group.get(), EC_KEY_get0_public_key(d_eckey.get()), POINT_CONVERSION_UNCOMPRESSED, reinterpret_cast<unsigned char*>(&binaryPoint.at(0)), binaryPoint.size(), nullptr);
1528
  if (ret == 0) {
1529
    throw runtime_error(getName() + " exporting point to binary failed");
1530
  }
1531

1532
  /* we skip the first byte as the other backends use
1533
     raw field elements, as opposed to the format described in
1534
     SEC1: "2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion" */
1535
  binaryPoint.erase(0, 1);
1536
  return binaryPoint;
1537
#endif
1538
}
6,639✔
1539

1540
#if OPENSSL_VERSION_MAJOR >= 3
1541
auto OpenSSLECDSADNSCryptoKeyEngine::makeKeyParams(const std::string& group_name, const BIGNUM* privateKey, const std::optional<std::string>& publicKey) const -> Params
1542
{
10,337✔
1543
  auto params_build = ParamsBuilder(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free);
10,337✔
1544
  if (params_build == nullptr) {
10,337!
1545
    throw pdns::OpenSSL::error(getName(), "Failed to create key's parameters builder");
×
1546
  }
×
1547

1548
  if ((!group_name.empty()) && OSSL_PARAM_BLD_push_utf8_string(params_build.get(), OSSL_PKEY_PARAM_GROUP_NAME, group_name.c_str(), group_name.length()) == 0) {
10,337!
1549
    throw pdns::OpenSSL::error(getName(), "Failed to create key's group parameter");
×
1550
  }
×
1551

1552
  if ((privateKey != nullptr) && OSSL_PARAM_BLD_push_BN(params_build.get(), OSSL_PKEY_PARAM_PRIV_KEY, privateKey) == 0) {
10,337!
1553
    throw pdns::OpenSSL::error(getName(), "Failed to create private key parameter");
×
1554
  }
×
1555

1556
  if (publicKey.has_value()) {
10,337!
1557
    if (OSSL_PARAM_BLD_push_octet_string(params_build.get(), OSSL_PKEY_PARAM_PUB_KEY, publicKey->c_str(), publicKey->length()) == 0) {
10,337!
1558
      throw pdns::OpenSSL::error(getName(), "Failed to create public key parameter");
×
1559
    }
×
1560
  }
10,337✔
1561

1562
  auto params = Params(OSSL_PARAM_BLD_to_param(params_build.get()), OSSL_PARAM_free);
10,337✔
1563
  if (params == nullptr) {
10,337!
1564
    throw pdns::OpenSSL::error(getName(), "Failed to create key's parameters");
×
1565
  }
×
1566

1567
  return params;
10,337✔
1568
}
10,337✔
1569
#endif
1570

1571
void OpenSSLECDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
1572
{
5,205✔
1573
  drc.d_algorithm = atoi(stormap["algorithm"].c_str());
5,205✔
1574

1575
  if (drc.d_algorithm != d_algorithm) {
5,205!
1576
    throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc.d_algorithm) + " to a " + std::to_string(d_algorithm) + " key");
×
1577
  }
×
1578

1579
  auto privateKey = mapToBN(getName(), stormap, "privatekey");
5,205✔
1580

1581
#if OPENSSL_VERSION_MAJOR >= 3
5,205✔
1582
  auto publicKeyECPoint = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
5,205✔
1583
  if (publicKeyECPoint == nullptr) {
5,205!
1584
    throw pdns::OpenSSL::error(getName(), "Failed to create public key point to import from ISC");
×
1585
  }
×
1586

1587
  if (EC_POINT_mul(d_group.get(), publicKeyECPoint.get(), privateKey.get(), nullptr, nullptr, nullptr) == 0) {
5,205!
1588
    throw pdns::OpenSSL::error(getName(), "Failed to derive public key from ISC private key");
×
1589
  }
×
1590

1591
  std::string publicKey{};
5,205✔
1592
  size_t bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_COMPRESSED, nullptr, 0, nullptr);
5,205✔
1593
  if (bufsize == 0) {
5,205!
1594
    throw pdns::OpenSSL::error(getName(), "Failed to get public key binary buffer size");
×
1595
  }
×
1596
  publicKey.resize(bufsize);
5,205✔
1597

1598
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1599
  auto* publicKeyData = reinterpret_cast<unsigned char*>(publicKey.data());
5,205✔
1600
  bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_COMPRESSED, publicKeyData, publicKey.length(), nullptr);
5,205✔
1601
  if (bufsize == 0) {
5,205!
1602
    throw pdns::OpenSSL::error(getName(), "Failed to convert public key to oct");
×
1603
  }
×
1604

1605
  auto params = makeKeyParams(d_group_name, privateKey.get(), std::make_optional(publicKey));
5,205✔
1606

1607
  auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free);
5,205✔
1608
  if (ctx == nullptr) {
5,205!
1609
    throw pdns::OpenSSL::error(getName(), "Could not create key context");
×
1610
  }
×
1611

1612
  if (EVP_PKEY_fromdata_init(ctx.get()) <= 0) {
5,205!
1613
    throw pdns::OpenSSL::error(getName(), "Could not initialize key context for loading data from ISC");
×
1614
  }
×
1615

1616
  EVP_PKEY* key = nullptr;
5,205✔
1617
  if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
5,205!
1618
    throw pdns::OpenSSL::error(getName(), "Could not create key from parameters");
×
1619
  }
×
1620

1621
  d_eckey.reset(key);
5,205✔
1622
#else
1623
  int ret = EC_KEY_set_private_key(d_eckey.get(), privateKey.get());
1624
  if (ret != 1) {
1625
    throw runtime_error(getName() + " setting private key failed");
1626
  }
1627

1628
  auto pub_key = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
1629
  if (!pub_key) {
1630
    throw runtime_error(getName() + " allocation of public key point failed");
1631
  }
1632

1633
  ret = EC_POINT_mul(d_group.get(), pub_key.get(), privateKey.get(), nullptr, nullptr, nullptr);
1634
  if (ret != 1) {
1635
    throw runtime_error(getName() + " computing public key from private failed");
1636
  }
1637

1638
  ret = EC_KEY_set_public_key(d_eckey.get(), pub_key.get());
1639
  if (ret != 1) {
1640
    throw runtime_error(getName() + " setting public key failed");
1641
  }
1642

1643
  EC_KEY_set_asn1_flag(d_eckey.get(), OPENSSL_EC_NAMED_CURVE);
1644
#endif
1645
}
5,205✔
1646

1647
bool OpenSSLECDSADNSCryptoKeyEngine::checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const
1648
{
44✔
1649
#if OPENSSL_VERSION_MAJOR >= 3
44✔
1650
  auto ctx = KeyContext{EVP_PKEY_CTX_new_from_pkey(nullptr, d_eckey.get(), nullptr), EVP_PKEY_CTX_free};
44✔
1651
  if (ctx == nullptr) {
44!
1652
    throw pdns::OpenSSL::error(getName(), "Failed to create context to check key");
×
1653
  }
×
1654

1655
  bool retval = true;
44✔
1656

1657
  auto addOpenSSLErrorMessageOnFail = [errorMessages, &retval](const int errorCode, const auto defaultErrorMessage) {
176✔
1658
    // Error code of -2 means the check is not supported for the algorithm, which is fine.
1659
    if (errorCode != 1 && errorCode != -2) {
176!
1660
      retval = false;
×
1661

1662
      if (errorMessages.has_value()) {
×
1663
        const auto* errorMessage = ERR_reason_error_string(ERR_get_error());
×
1664
        if (errorMessage == nullptr) {
×
1665
          errorMessages->get().push_back(defaultErrorMessage);
×
1666
        }
×
1667
        else {
×
1668
          errorMessages->get().emplace_back(errorMessage);
×
1669
        }
×
1670
      }
×
1671
    }
×
1672
  };
176✔
1673

1674
  addOpenSSLErrorMessageOnFail(EVP_PKEY_param_check(ctx.get()), getName() + "Unknown OpenSSL error during key param check");
44✔
1675
  addOpenSSLErrorMessageOnFail(EVP_PKEY_public_check(ctx.get()), getName() + "Unknown OpenSSL error during public key check");
44✔
1676
  addOpenSSLErrorMessageOnFail(EVP_PKEY_private_check(ctx.get()), getName() + "Unknown OpenSSL error during private key check");
44✔
1677
  addOpenSSLErrorMessageOnFail(EVP_PKEY_pairwise_check(ctx.get()), getName() + "Unknown OpenSSL error during key pairwise check");
44✔
1678

1679
  return retval;
44✔
1680
#else
1681
  bool retval = true;
1682
  if (EC_KEY_check_key(d_eckey.get()) != 1) {
1683
    retval = false;
1684
    if (errorMessages.has_value()) {
1685
      const auto* errmsg = ERR_reason_error_string(ERR_get_error());
1686
      if (errmsg == nullptr) {
1687
        errmsg = "Unknown OpenSSL error";
1688
      }
1689
      errorMessages->get().push_back(errmsg);
1690
    }
1691
  }
1692
  return retval;
1693
#endif
1694
}
44✔
1695

1696
void OpenSSLECDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& content)
1697
{
5,132✔
1698
#if OPENSSL_VERSION_MAJOR >= 3
5,132✔
1699
  /* uncompressed point, from SEC1: "2.3.4 Octet-String-to-Elliptic-Curve-Point
1700
   * Conversion"
1701
   */
1702
  std::string publicKey = "\x04";
5,132✔
1703
  publicKey.append(content);
5,132✔
1704

1705
  auto params = makeKeyParams(d_group_name, nullptr, std::make_optional(publicKey));
5,132✔
1706

1707
  auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free);
5,132✔
1708
  if (ctx == nullptr) {
5,132!
1709
    throw pdns::OpenSSL::error(getName(), "Failed to create key context");
×
1710
  }
×
1711

1712
  if (EVP_PKEY_fromdata_init(ctx.get()) <= 0) {
5,132!
1713
    throw pdns::OpenSSL::error(getName(), "Failed to initialize key context for loading data from ISC");
×
1714
  }
×
1715

1716
  EVP_PKEY* key = nullptr;
5,132✔
1717
  if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_PUBLIC_KEY, params.get()) <= 0) {
5,132!
1718
    throw pdns::OpenSSL::error(getName(), "Failed to create key from parameters");
×
1719
  }
×
1720

1721
  d_eckey.reset(key);
5,132✔
1722
#else
1723
  /* uncompressed point, from SEC1: "2.3.4 Octet-String-to-Elliptic-Curve-Point
1724
   * Conversion"
1725
   */
1726
  string ecdsaPoint = "\x04";
1727
  ecdsaPoint.append(content);
1728

1729
  auto pub_key = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
1730
  if (!pub_key) {
1731
    throw runtime_error(getName() + " allocation of point structure failed");
1732
  }
1733

1734
  int ret = EC_POINT_oct2point(d_group.get(), pub_key.get(), (unsigned char*)ecdsaPoint.c_str(), ecdsaPoint.length(), nullptr);
1735
  if (ret != 1) {
1736
    throw runtime_error(getName() + " reading ECP point from binary failed");
1737
  }
1738

1739
  ret = EC_KEY_set_private_key(d_eckey.get(), nullptr);
1740
  if (ret == 1) {
1741
    throw runtime_error(getName() + " setting private key failed");
1742
  }
1743

1744
  ret = EC_KEY_set_public_key(d_eckey.get(), pub_key.get());
1745
  if (ret != 1) {
1746
    throw runtime_error(getName() + " setting public key failed");
1747
  }
1748
#endif
1749
}
5,132✔
1750
#endif
1751

1752
#ifdef HAVE_LIBCRYPTO_EDDSA
1753
class OpenSSLEDDSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
1754
{
1755
public:
1756
  explicit OpenSSLEDDSADNSCryptoKeyEngine(unsigned int algo);
1757

1758
  [[nodiscard]] string getName() const override { return "OpenSSL EdDSA"; }
71✔
1759
  [[nodiscard]] int getBits() const override;
1760

1761
  void create(unsigned int bits) override;
1762

1763
  /**
1764
   * \brief Creates an EDDSA key engine from a PEM file.
1765
   *
1766
   * Receives an open file handle with PEM contents and creates an EDDSA key engine.
1767
   *
1768
   * \param[in] drc Key record contents to be populated.
1769
   *
1770
   * \param[in] inputFile An open file handle to a file containing EDDSA PEM contents.
1771
   *
1772
   * \param[in] filename Only used for providing filename information in error messages.
1773
   *
1774
   * \return An EDDSA key engine populated with the contents of the PEM file.
1775
   */
1776
  void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
1777

1778
  /**
1779
   * \brief Writes this key's contents to a file.
1780
   *
1781
   * Receives an open file handle and writes this key's contents to the
1782
   * file.
1783
   *
1784
   * \param[in] outputFile An open file handle for writing.
1785
   *
1786
   * \exception std::runtime_error In case of OpenSSL errors.
1787
   */
1788
  void convertToPEMFile(std::FILE& outputFile) const override;
1789

1790
  [[nodiscard]] storvector_t convertToISCVector() const override;
1791
  [[nodiscard]] std::string sign(const std::string& msg) const override;
1792
  [[nodiscard]] bool verify(const std::string& message, const std::string& signature) const override;
1793
  [[nodiscard]] std::string getPublicKeyString() const override;
1794
  void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
1795
  void fromPublicKeyString(const std::string& content) override;
1796
  [[nodiscard]] bool checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const override;
1797

1798
  static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
1799
  {
35✔
1800
    return make_unique<OpenSSLEDDSADNSCryptoKeyEngine>(algorithm);
35✔
1801
  }
35✔
1802

1803
  using Key = unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
1804
  using KeyContext = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
1805
  using MessageDigestContext = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>;
1806

1807
private:
1808
  size_t d_len{0};
1809
  int d_id{0};
1810

1811
  Key d_edkey;
1812
};
1813

1814
OpenSSLEDDSADNSCryptoKeyEngine::OpenSSLEDDSADNSCryptoKeyEngine(unsigned int algo) :
1815
  DNSCryptoKeyEngine(algo),
1816
  d_edkey(Key(nullptr, EVP_PKEY_free))
1817
{
35✔
1818
  int ret = RAND_status();
35✔
1819
  if (ret != 1) {
35!
1820
    throw runtime_error(getName() + " insufficient entropy");
×
1821
  }
×
1822

1823
#ifdef HAVE_LIBCRYPTO_ED25519
35✔
1824
  if (d_algorithm == 15) {
35✔
1825
    d_len = 32;
17✔
1826
    d_id = NID_ED25519;
17✔
1827
  }
17✔
1828
#endif
35✔
1829
#ifdef HAVE_LIBCRYPTO_ED448
35✔
1830
  if (d_algorithm == 16) {
35✔
1831
    d_len = 57;
18✔
1832
    d_id = NID_ED448;
18✔
1833
  }
18✔
1834
#endif
35✔
1835
  if (d_len == 0) {
35!
1836
    throw runtime_error(getName() + " unknown algorithm " + std::to_string(d_algorithm));
×
1837
  }
×
1838
}
35✔
1839

1840
int OpenSSLEDDSADNSCryptoKeyEngine::getBits() const
1841
{
13✔
1842
  return (int)d_len << 3;
13✔
1843
}
13✔
1844

1845
bool OpenSSLEDDSADNSCryptoKeyEngine::checkKey([[maybe_unused]] std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const
1846
{
13✔
1847
#if OPENSSL_VERSION_MAJOR >= 3
13✔
1848
  auto ctx = KeyContext{EVP_PKEY_CTX_new_from_pkey(nullptr, d_edkey.get(), nullptr), EVP_PKEY_CTX_free};
13✔
1849
  if (ctx == nullptr) {
13!
1850
    throw pdns::OpenSSL::error(getName(), "Failed to create context to check key");
×
1851
  }
×
1852

1853
  bool retval = true;
13✔
1854

1855
  auto addOpenSSLErrorMessageOnFail = [errorMessages, &retval](const int errorCode, const auto defaultErrorMessage) {
52✔
1856
    // Error code of -2 means the check is not supported for the algorithm, which is fine.
1857
    if (errorCode != 1 && errorCode != -2) {
52!
1858
      retval = false;
×
1859

1860
      if (errorMessages.has_value()) {
×
1861
        const auto* errorMessage = ERR_reason_error_string(ERR_get_error());
×
1862
        if (errorMessage == nullptr) {
×
1863
          errorMessages->get().push_back(defaultErrorMessage);
×
1864
        }
×
1865
        else {
×
1866
          errorMessages->get().emplace_back(errorMessage);
×
1867
        }
×
1868
      }
×
1869
    }
×
1870
  };
52✔
1871

1872
  addOpenSSLErrorMessageOnFail(EVP_PKEY_param_check(ctx.get()), getName() + "Unknown OpenSSL error during key param check");
13✔
1873
  addOpenSSLErrorMessageOnFail(EVP_PKEY_public_check(ctx.get()), getName() + "Unknown OpenSSL error during public key check");
13✔
1874
  addOpenSSLErrorMessageOnFail(EVP_PKEY_private_check(ctx.get()), getName() + "Unknown OpenSSL error during private key check");
13✔
1875
  addOpenSSLErrorMessageOnFail(EVP_PKEY_pairwise_check(ctx.get()), getName() + "Unknown OpenSSL error during key pairwise check");
13✔
1876

1877
  return retval;
13✔
1878
#else
1879
  return (d_edkey ? true : false);
1880
#endif
1881
}
13✔
1882

1883
void OpenSSLEDDSADNSCryptoKeyEngine::create(unsigned int /* bits */)
1884
{
500✔
1885
  auto pctx = KeyContext(EVP_PKEY_CTX_new_id(d_id, nullptr), EVP_PKEY_CTX_free);
500✔
1886
  if (!pctx) {
500!
1887
    throw pdns::OpenSSL::error(getName(), "Context initialization failed");
×
1888
  }
×
1889

1890
  if (EVP_PKEY_keygen_init(pctx.get()) < 1) {
500!
1891
    throw pdns::OpenSSL::error(getName(), "Keygen initialization failed");
×
1892
  }
×
1893

1894
  EVP_PKEY* newKey = nullptr;
500✔
1895
  if (EVP_PKEY_keygen(pctx.get(), &newKey) < 1) {
500!
1896
    throw pdns::OpenSSL::error(getName(), "Key generation failed");
×
1897
  }
×
1898

1899
  d_edkey.reset(newKey);
500✔
1900
}
500✔
1901

1902
void OpenSSLEDDSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename)
1903
{
4✔
1904
  drc.d_algorithm = d_algorithm;
4✔
1905
  d_edkey = Key(PEM_read_PrivateKey(&inputFile, nullptr, nullptr, nullptr), &EVP_PKEY_free);
4✔
1906
  if (d_edkey == nullptr) {
4!
1907
    if (filename.has_value()) {
×
1908
      throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM file `" + filename->get() + "`");
×
1909
    }
×
1910

1911
    throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM contents");
×
1912
  }
×
1913
}
4✔
1914

1915
void OpenSSLEDDSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
1916
{
8✔
1917
  auto ret = PEM_write_PrivateKey(&outputFile, d_edkey.get(), nullptr, nullptr, 0, nullptr, nullptr);
8✔
1918
  if (ret == 0) {
8!
1919
    throw pdns::OpenSSL::error(getName(), "Could not convert private key to PEM");
×
1920
  }
×
1921
}
8✔
1922

1923
DNSCryptoKeyEngine::storvector_t OpenSSLEDDSADNSCryptoKeyEngine::convertToISCVector() const
1924
{
13✔
1925
  storvector_t storvect;
13✔
1926
  string algorithm;
13✔
1927

1928
#ifdef HAVE_LIBCRYPTO_ED25519
13✔
1929
  if (d_algorithm == 15) {
13✔
1930
    algorithm = "15 (ED25519)";
6✔
1931
  }
6✔
1932
#endif
13✔
1933
#ifdef HAVE_LIBCRYPTO_ED448
13✔
1934
  if (d_algorithm == 16) {
13✔
1935
    algorithm = "16 (ED448)";
7✔
1936
  }
7✔
1937
#endif
13✔
1938
  if (algorithm.empty()) {
13!
1939
    algorithm = " ? (?)";
×
1940
  }
×
1941

1942
  storvect.emplace_back("Algorithm", algorithm);
13✔
1943

1944
  string buf;
13✔
1945
  size_t len = d_len;
13✔
1946
  buf.resize(len);
13✔
1947

1948
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1949
  if (EVP_PKEY_get_raw_private_key(d_edkey.get(), reinterpret_cast<unsigned char*>(&buf.at(0)), &len) < 1) {
13!
1950
    throw pdns::OpenSSL::error(getName(), "Could not get private key from d_edkey");
×
1951
  }
×
1952
  storvect.emplace_back("PrivateKey", buf);
13✔
1953
  return storvect;
13✔
1954
}
13✔
1955

1956
std::string OpenSSLEDDSADNSCryptoKeyEngine::sign(const std::string& msg) const
1957
{
516✔
1958
  auto mdctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
516✔
1959
  if (!mdctx) {
516!
1960
    throw pdns::OpenSSL::error(getName(), "MD context initialization failed");
×
1961
  }
×
1962
  if (EVP_DigestSignInit(mdctx.get(), nullptr, nullptr, nullptr, d_edkey.get()) < 1) {
516!
1963
    throw pdns::OpenSSL::error(getName(), "Unable to initialize signer");
×
1964
  }
×
1965

1966
  string msgToSign = msg;
516✔
1967

1968
  size_t siglen = d_len * 2;
516✔
1969
  string signature;
516✔
1970
  signature.resize(siglen);
516✔
1971

1972
  if (EVP_DigestSign(mdctx.get(),
516!
1973
                     // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1974
                     reinterpret_cast<unsigned char*>(&signature.at(0)), &siglen,
516✔
1975
                     // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1976
                     reinterpret_cast<unsigned char*>(&msgToSign.at(0)), msgToSign.length())
516✔
1977
      < 1) {
516✔
1978
    throw pdns::OpenSSL::error(getName(), "Signing error");
×
1979
  }
×
1980

1981
  return signature;
516✔
1982
}
516✔
1983

1984
bool OpenSSLEDDSADNSCryptoKeyEngine::verify(const std::string& message, const std::string& signature) const
1985
{
516✔
1986
  auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
516✔
1987
  if (!ctx) {
516!
1988
    throw pdns::OpenSSL::error(getName(), "MD context initialization failed");
×
1989
  }
×
1990
  if (EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, d_edkey.get()) < 1) {
516!
1991
    throw pdns::OpenSSL::error(getName(), "Unable to initialize signer");
×
1992
  }
×
1993

1994
  auto ret = EVP_DigestVerify(ctx.get(),
516✔
1995
                              // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1996
                              reinterpret_cast<const unsigned char*>(&signature.at(0)), signature.length(),
516✔
1997
                              // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1998
                              reinterpret_cast<const unsigned char*>(&message.at(0)), message.length());
516✔
1999
  if (ret < 0) {
516!
2000
    throw pdns::OpenSSL::error(getName(), "Verification failure");
×
2001
  }
×
2002

2003
  return (ret == 1);
516✔
2004
}
516✔
2005

2006
std::string OpenSSLEDDSADNSCryptoKeyEngine::getPublicKeyString() const
2007
{
31✔
2008
  string buf;
31✔
2009
  size_t len = d_len;
31✔
2010
  buf.resize(len);
31✔
2011

2012
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
2013
  if (EVP_PKEY_get_raw_public_key(d_edkey.get(), reinterpret_cast<unsigned char*>(&buf.at(0)), &len) < 1) {
31!
2014
    throw pdns::OpenSSL::error(getName(), "Unable to get public key from key struct");
×
2015
  }
×
2016

2017
  return buf;
31✔
2018
}
31✔
2019

2020
void OpenSSLEDDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
2021
{
17✔
2022
  drc.d_algorithm = atoi(stormap["algorithm"].c_str());
17✔
2023
  if (drc.d_algorithm != d_algorithm) {
17!
2024
    throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc.d_algorithm) + " to a " + std::to_string(d_algorithm) + " key");
×
2025
  }
×
2026

2027
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
2028
  d_edkey = Key(EVP_PKEY_new_raw_private_key(d_id, nullptr, reinterpret_cast<unsigned char*>(&stormap["privatekey"].at(0)), stormap["privatekey"].length()), EVP_PKEY_free);
17✔
2029
  if (!d_edkey) {
17!
2030
    throw pdns::OpenSSL::error(getName(), "Could not create key structure from private key");
×
2031
  }
×
2032
}
17✔
2033

2034
void OpenSSLEDDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& content)
2035
{
5✔
2036
  if (content.length() != d_len) {
5!
2037
    throw runtime_error(getName() + " wrong public key length for algorithm " + std::to_string(d_algorithm));
×
2038
  }
×
2039

2040
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
2041
  const auto* raw = reinterpret_cast<const unsigned char*>(content.c_str());
5✔
2042

2043
  d_edkey = Key(EVP_PKEY_new_raw_public_key(d_id, nullptr, raw, d_len), EVP_PKEY_free);
5✔
2044
  if (!d_edkey) {
5!
2045
    throw pdns::OpenSSL::error(getName(), "Allocation of public key structure failed");
×
2046
  }
×
2047
}
5✔
2048
#endif // HAVE_LIBCRYPTO_EDDSA
2049

2050
namespace
2051
{
2052
const struct LoaderStruct
2053
{
2054
  LoaderStruct()
2055
  {
6,421✔
2056
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
6,421✔
2057
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1NSEC3SHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
6,421✔
2058
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA256, &OpenSSLRSADNSCryptoKeyEngine::maker);
6,421✔
2059
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA512, &OpenSSLRSADNSCryptoKeyEngine::maker);
6,421✔
2060
#ifdef HAVE_LIBCRYPTO_ECDSA
6,421✔
2061
    DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA256, &OpenSSLECDSADNSCryptoKeyEngine::maker);
6,421✔
2062
    DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA384, &OpenSSLECDSADNSCryptoKeyEngine::maker);
6,421✔
2063
#endif
6,421✔
2064
#ifdef HAVE_LIBCRYPTO_ED25519
6,421✔
2065
    DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
6,421✔
2066
#endif
6,421✔
2067
#ifdef HAVE_LIBCRYPTO_ED448
6,421✔
2068
    DNSCryptoKeyEngine::report(DNSSECKeeper::ED448, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
6,421✔
2069
#endif
6,421✔
2070
  }
6,421✔
2071
} loaderOpenSSL;
2072
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc