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

PowerDNS / pdns / 9498012098

13 Jun 2024 10:27AM UTC coverage: 61.22% (-3.4%) from 64.615%
9498012098

Pull #14325

github

web-flow
Merge e3409d021 into 57ab0fbc9
Pull Request #14325: Set nsec3param: first increase the serial and then rectify

34064 of 88172 branches covered (38.63%)

Branch coverage included in aggregate %.

5 of 7 new or added lines in 1 file covered. (71.43%)

5415 existing lines in 81 files now uncovered.

118803 of 161528 relevant lines covered (73.55%)

4993315.9 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

168
#endif /* HAVE_RSA_GET0_KEY */
169

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

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

181
  unsigned int r;
3,405✔
182
  for (int i = 0; i < 1024; i += 4) {
875,085✔
183
    r = dns_random_uint32();
871,680✔
184
    entropy.append((const char*)&r, 4);
871,680✔
185
  }
871,680✔
186

187
  RAND_seed((const unsigned char*)entropy.c_str(), 1024);
3,405✔
188
}
3,405✔
189

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

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

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

203
  return number;
10,353✔
204
}
10,353✔
205

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

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

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

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

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

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

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

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

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

270
  using Params = std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)>;
271
  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;
272
#endif
273

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

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

290
  Key d_key;
291
};
292

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

498
#if OPENSSL_VERSION_MAJOR >= 3
499
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
500
{
1,054✔
501
  auto params_build = ParamsBuilder(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free);
1,054✔
502
  if (params_build == nullptr) {
1,054!
503
    throw pdns::OpenSSL::error(getName(), "Could not create key's parameters builder");
×
504
  }
×
505

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

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

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

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

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

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

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

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

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

543
  return params;
1,054✔
544
}
1,054✔
545
#endif
546

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

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

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

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

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

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

624
  return storvect;
192✔
625
}
192✔
626

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

642
const EVP_MD* OpenSSLRSADNSCryptoKeyEngine::hasher() const
643
{
1,873✔
644
  const EVP_MD* messageDigest = nullptr;
1,873✔
645

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

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

665
  return messageDigest;
1,867✔
666
}
1,867✔
667

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

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

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

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

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

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

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

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

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

734
  signature.resize(signatureLen);
1,165✔
735

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

747
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
748
  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());
749
  if (res != 1) {
750
    throw runtime_error(getName() + " failed to generate signature");
751
  }
752

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

756
  return signature;
1,165✔
757
}
1,165✔
758

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

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

771
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
772
  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());
707✔
773
  if (ret < 0) {
707!
774
    throw pdns::OpenSSL::error(getName(), "Failed to verify message signature");
×
775
  }
×
776

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

782
  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());
783

784
  return (ret == 1);
785
#endif
786
}
707✔
787

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

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

805
  string keystring;
926✔
806
  std::string tmp;
926✔
807
  tmp.resize(std::max(BN_num_bytes(publicExponent), BN_num_bytes(modulus)));
926✔
808

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

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

826
  return keystring;
926✔
827
}
926✔
828

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

835
  auto prime1 = mapToBN(getName(), stormap, "prime1");
749✔
836
  auto prime2 = mapToBN(getName(), stormap, "prime2");
749✔
837

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

842
  pdns::checked_stoi_into(drc.d_algorithm, stormap["algorithm"]);
749✔
843

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

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

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

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

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

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

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

877
  d_key = std::move(key);
878
#endif
879
}
749✔
880

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1000
  [[nodiscard]] string getName() const override { return "OpenSSL ECDSA"; }
4,481✔
1001
  [[nodiscard]] int getBits() const override;
1002

1003
  void create(unsigned int bits) override;
1004

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

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

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

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

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

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

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

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

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

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

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

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

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

1106
  int d_id{0};
8,180✔
1107

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1241
  if (d_algorithm == 13) {
722✔
1242
    algorithm = "13 (ECDSAP256SHA256)";
689✔
1243
  }
689✔
1244
  else if (d_algorithm == 14) {
33!
1245
    algorithm = "14 (ECDSAP384SHA384)";
33✔
1246
  }
33✔
1247
  else {
×
1248
    algorithm = " ? (?)";
×
1249
  }
×
1250

1251
  storvect.emplace_back("Algorithm", algorithm);
722✔
1252

1253
#if OPENSSL_VERSION_MAJOR >= 3
722✔
1254
  auto privateKeyBN = getPrivateKey();
722✔
1255

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

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

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

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

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

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

1288
  return storvect;
722✔
1289
}
722✔
1290

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

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

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

1312
const EVP_MD* OpenSSLECDSADNSCryptoKeyEngine::hasher() const
1313
{
696,017✔
1314
  const EVP_MD* messageDigest = nullptr;
696,017✔
1315

1316
  switch (d_algorithm) {
696,017✔
1317
  case DNSSECKeeper::ECDSA256:
694,939✔
1318
    messageDigest = EVP_sha256();
694,939✔
1319
    break;
694,939✔
1320
  case DNSSECKeeper::ECDSA384:
664✔
1321
    messageDigest = EVP_sha384();
664✔
1322
    break;
664✔
1323
  default:
×
1324
    throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm));
×
1325
  }
696,017✔
1326

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

1331
  return messageDigest;
694,583✔
1332
}
694,583✔
1333

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

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

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

1358
  std::size_t signatureLen = 0;
693,973✔
1359

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

1366
  std::string signatureBuffer;
693,973✔
1367
  signatureBuffer.resize(signatureLen);
693,973✔
1368

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

1375
  signatureBuffer.resize(signatureLen);
693,973✔
1376

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

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

1391
  string ret;
693,973✔
1392
  std::string tmp;
693,973✔
1393
  tmp.resize(d_len);
693,973✔
1394

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

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

1412
  return ret;
693,973✔
1413
}
693,973✔
1414

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

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

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

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

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

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

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

1459
  return (ret == 1);
2,489✔
1460
#else
1461
  string l_hash = this->hash(message);
1462

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

1468
  return (ret == 1);
1469
#endif
1470
}
2,489✔
1471

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

1480
  std::string publicKey{};
5,700✔
1481
  publicKey.resize(bufsize);
5,700✔
1482

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

1489
  publicKey.resize(bufsize);
5,700✔
1490

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

1496
  auto ctx = BigNumContext(BN_CTX_new(), BN_CTX_free);
5,700✔
1497

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

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

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

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

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

1528
  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);
1529
  if (ret == 0) {
1530
    throw runtime_error(getName() + " exporting point to binary failed");
1531
  }
1532

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

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

1549
  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) {
6,641!
1550
    throw pdns::OpenSSL::error(getName(), "Failed to create key's group parameter");
×
1551
  }
×
1552

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

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

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

1568
  return params;
6,641✔
1569
}
6,641✔
1570
#endif
1571

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

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

1580
  auto privateKey = mapToBN(getName(), stormap, "privatekey");
4,361✔
1581

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

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

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

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

1606
  auto params = makeKeyParams(d_group_name, privateKey.get(), std::make_optional(publicKey));
4,361✔
1607

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

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

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

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

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

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

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

1644
  EC_KEY_set_asn1_flag(d_eckey.get(), OPENSSL_EC_NAMED_CURVE);
1645
#endif
1646
}
4,361✔
1647

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

1656
  bool retval = true;
27✔
1657

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

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

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

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

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

1706
  auto params = makeKeyParams(d_group_name, nullptr, std::make_optional(publicKey));
2,280✔
1707

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

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

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

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

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

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

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

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

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

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

1762
  void create(unsigned int bits) override;
1763

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

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

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

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

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

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

1812
  Key d_edkey;
1813
};
1814

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

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

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

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

1854
  bool retval = true;
19✔
1855

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

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

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

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

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

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

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

1900
  d_edkey.reset(newKey);
1,300✔
1901
}
1,300✔
1902

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

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

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

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

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

1943
  storvect.emplace_back("Algorithm", algorithm);
19✔
1944

1945
  string buf;
19✔
1946
  size_t len = d_len;
19✔
1947
  buf.resize(len);
19✔
1948

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

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

1967
  string msgToSign = msg;
1,312✔
1968

1969
  size_t siglen = d_len * 2;
1,312✔
1970
  string signature;
1,312✔
1971
  signature.resize(siglen);
1,312✔
1972

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

1982
  return signature;
1,312✔
1983
}
1,312✔
1984

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

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

2004
  return (ret == 1);
1,312✔
2005
}
1,312✔
2006

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

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

2018
  return buf;
51✔
2019
}
51✔
2020

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

2028
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
2029
  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);
22✔
2030
  if (!d_edkey) {
22!
2031
    throw pdns::OpenSSL::error(getName(), "Could not create key structure from private key");
×
2032
  }
×
2033
}
22✔
2034

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

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

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

2051
namespace
2052
{
2053
const struct LoaderStruct
2054
{
2055
  LoaderStruct()
2056
  {
3,509✔
2057
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
3,509✔
2058
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1NSEC3SHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
3,509✔
2059
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA256, &OpenSSLRSADNSCryptoKeyEngine::maker);
3,509✔
2060
    DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA512, &OpenSSLRSADNSCryptoKeyEngine::maker);
3,509✔
2061
#ifdef HAVE_LIBCRYPTO_ECDSA
3,509✔
2062
    DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA256, &OpenSSLECDSADNSCryptoKeyEngine::maker);
3,509✔
2063
    DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA384, &OpenSSLECDSADNSCryptoKeyEngine::maker);
3,509✔
2064
#endif
3,509✔
2065
#ifdef HAVE_LIBCRYPTO_ED25519
3,509✔
2066
    DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
3,509✔
2067
#endif
3,509✔
2068
#ifdef HAVE_LIBCRYPTO_ED448
3,509✔
2069
    DNSCryptoKeyEngine::report(DNSSECKeeper::ED448, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
3,509✔
2070
#endif
3,509✔
2071
  }
3,509✔
2072
} loaderOpenSSL;
2073
}
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