]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/opensslsigners.cc
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[thirdparty/pdns.git] / pdns / opensslsigners.cc
CommitLineData
12471842
PL
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 */
f5b73a82
FM
22#include "misc.hh"
23#include <memory>
24#include <openssl/crypto.h>
25#include <openssl/ec.h>
26#include <optional>
7581a35b
KM
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30#include <openssl/obj_mac.h>
74d83458 31#ifdef HAVE_LIBCRYPTO_ECDSA
7581a35b 32#include <openssl/ecdsa.h>
de4e1d0d 33#endif
439974d7
PL
34#if defined(HAVE_LIBCRYPTO_ED25519) || defined(HAVE_LIBCRYPTO_ED448)
35#include <openssl/evp.h>
36#endif
fb46af61 37#include <openssl/bn.h>
7581a35b
KM
38#include <openssl/sha.h>
39#include <openssl/rand.h>
a41d80d1 40#include <openssl/rsa.h>
eeddacf7
FM
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
d366601f 47#include <openssl/opensslv.h>
ac3f3893 48#include <openssl/err.h>
87066241 49#include <openssl/pem.h>
42f01311 50#include "opensslsigners.hh"
7581a35b 51#include "dnssecinfra.hh"
86783480 52#include "dnsseckeeper.hh"
7581a35b 53
629440d4 54#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL)
d366601f 55/* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */
69b8131b
RG
56
57#include "lock.hh"
58static std::vector<std::mutex> openssllocks;
42f01311 59
f4bf7b65 60extern "C"
42f01311 61{
f4bf7b65
FM
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 }
42f01311 70 }
42f01311 71
f4bf7b65
FM
72 static unsigned long openssl_pthreads_id_callback(void)
73 {
74 return (unsigned long)pthread_self();
75 }
42f01311 76}
77
78void openssl_thread_setup()
79{
69b8131b
RG
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);
42f01311 83}
84
85void openssl_thread_cleanup()
86{
69b8131b
RG
87 CRYPTO_set_locking_callback(nullptr);
88 openssllocks.clear();
42f01311 89}
888bc297 90
629440d4 91#ifndef HAVE_RSA_GET0_KEY
1648b8ff 92/* those symbols are defined in LibreSSL 2.7.0+ */
888bc297 93/* compat helpers. These DO NOT do any of the checking that the libssl 1.1 functions do. */
8f6335a5
FM
94static inline void RSA_get0_key(const RSA* rsakey, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d)
95{
888bc297
CH
96 *n = rsakey->n;
97 *e = rsakey->e;
98 *d = rsakey->d;
99}
100
8f6335a5
FM
101static inline int RSA_set0_key(RSA* rsakey, BIGNUM* n, BIGNUM* e, BIGNUM* d)
102{
888bc297
CH
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
8f6335a5
FM
118static inline void RSA_get0_factors(const RSA* rsakey, const BIGNUM** p, const BIGNUM** q)
119{
888bc297
CH
120 *p = rsakey->p;
121 *q = rsakey->q;
122}
123
8f6335a5
FM
124static inline int RSA_set0_factors(RSA* rsakey, BIGNUM* p, BIGNUM* q)
125{
888bc297
CH
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
8f6335a5
FM
133static inline void RSA_get0_crt_params(const RSA* rsakey, const BIGNUM** dmp1, const BIGNUM** dmq1, const BIGNUM** iqmp)
134{
888bc297
CH
135 *dmp1 = rsakey->dmp1;
136 *dmq1 = rsakey->dmq1;
137 *iqmp = rsakey->iqmp;
138}
139
8f6335a5
FM
140static inline int RSA_set0_crt_params(RSA* rsakey, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp)
141{
888bc297
CH
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
aa74d164 151#ifdef HAVE_LIBCRYPTO_ECDSA
37fa09a3
FM
152static inline void ECDSA_SIG_get0(const ECDSA_SIG* signature, const BIGNUM** pr, const BIGNUM** ps)
153{
888bc297
CH
154 *pr = signature->r;
155 *ps = signature->s;
156}
157
37fa09a3
FM
158static inline int ECDSA_SIG_set0(ECDSA_SIG* signature, BIGNUM* pr, BIGNUM* ps)
159{
888bc297
CH
160 BN_clear_free(signature->r);
161 BN_clear_free(signature->s);
162 signature->r = pr;
163 signature->s = ps;
164 return 1;
165}
aa74d164
RG
166#endif /* HAVE_LIBCRYPTO_ECDSA */
167
629440d4 168#endif /* HAVE_RSA_GET0_KEY */
1648b8ff 169
d366601f
CH
170#else
171void openssl_thread_setup() {}
172void openssl_thread_cleanup() {}
173#endif
42f01311 174
175/* seeding PRNG */
42f01311 176void openssl_seed()
177{
178 std::string entropy;
179 entropy.reserve(1024);
180
181 unsigned int r;
f4bf7b65 182 for (int i = 0; i < 1024; i += 4) {
e556776d 183 r = dns_random_uint32();
42f01311 184 entropy.append((const char*)&r, 4);
185 }
186
187 RAND_seed((const unsigned char*)entropy.c_str(), 1024);
188}
189
eeddacf7
FM
190using BigNum = unique_ptr<BIGNUM, decltype(&BN_clear_free)>;
191
192static auto mapToBN(const std::string& componentName, const std::map<std::string, std::string>& stormap, const std::string& key) -> BigNum
a41d80d1 193{
eeddacf7 194 const std::string& value = stormap.at(key);
a41d80d1 195
eeddacf7
FM
196 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
197 const auto* valueCStr = reinterpret_cast<const unsigned char*>(value.c_str());
198 auto number = BigNum{BN_bin2bn(valueCStr, static_cast<int>(value.length()), nullptr), BN_clear_free};
199 if (number == nullptr) {
200 throw pdns::OpenSSL::error(componentName, "Failed to parse key `" + key + "`");
a41d80d1
RG
201 }
202
eeddacf7
FM
203 return number;
204}
d12b259b 205
eeddacf7
FM
206class OpenSSLRSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
207{
208public:
209 explicit OpenSSLRSADNSCryptoKeyEngine(unsigned int algo);
210
211 [[nodiscard]] string getName() const override { return "OpenSSL RSA"; }
212 [[nodiscard]] int getBits() const override;
d12b259b 213 void create(unsigned int bits) override;
fd421a48 214
b67ad172
FM
215 /**
216 * \brief Creates an RSA key engine from a PEM file.
217 *
a538f953 218 * Receives an open file handle with PEM contents and creates an RSA key engine.
b67ad172
FM
219 *
220 * \param[in] drc Key record contents to be populated.
221 *
49faa6a4 222 * \param[in] inputFile An open file handle to a file containing RSA PEM contents.
b67ad172 223 *
a538f953
FM
224 * \param[in] filename Only used for providing filename information in error messages.
225 *
b67ad172
FM
226 * \return An RSA key engine populated with the contents of the PEM file.
227 */
c97af739 228 void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
b67ad172 229
fd421a48
FM
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 *
49faa6a4 236 * \param[in] outputFile An open file handle for writing.
fd421a48
FM
237 *
238 * \exception std::runtime_error In case of OpenSSL errors.
239 */
a8b5e8ba 240 void convertToPEMFile(std::FILE& outputFile) const override;
49faa6a4
FM
241
242 [[nodiscard]] storvector_t convertToISCVector() const override;
eeddacf7
FM
243
244 // TODO Fred: hash() can probably be completely removed. See #12464.
49faa6a4
FM
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;
eeddacf7 249
d12b259b
CH
250 void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
251 void fromPublicKeyString(const std::string& content) override;
f7b9d3b7 252 [[nodiscard]] bool checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const override;
a41d80d1 253
a2c6e554 254 static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
a41d80d1 255 {
a2c6e554 256 return make_unique<OpenSSLRSADNSCryptoKeyEngine>(algorithm);
a41d80d1
RG
257 }
258
259private:
eeddacf7
FM
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;
349384bf
RG
278 static int hashSizeToKind(size_t hashSize);
279
eeddacf7
FM
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)>;
eeddacf7
FM
288#endif
289
290 Key d_key;
a41d80d1
RG
291};
292
eeddacf7
FM
293OpenSSLRSADNSCryptoKeyEngine::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{
301 int ret = RAND_status();
302 if (ret != 1) {
303 throw runtime_error(getName() + " insufficient entropy");
304 }
305}
306
307int OpenSSLRSADNSCryptoKeyEngine::getBits() const
308{
309#if OPENSSL_VERSION_MAJOR >= 3
310 return EVP_PKEY_get_bits(d_key.get());
311#else
312 return RSA_size(d_key.get()) << 3;
313#endif
314}
315
a41d80d1
RG
316void OpenSSLRSADNSCryptoKeyEngine::create(unsigned int bits)
317{
ac3f3893 318 // When changing the bitsizes, also edit them in ::checkKey
86783480
CH
319 if ((d_algorithm == DNSSECKeeper::RSASHA1 || d_algorithm == DNSSECKeeper::RSASHA1NSEC3SHA1) && (bits < 512 || bits > 4096)) {
320 /* RFC3110 */
8f6335a5 321 throw runtime_error(getName() + " RSASHA1 key generation failed for invalid bits size " + std::to_string(bits));
86783480
CH
322 }
323 if (d_algorithm == DNSSECKeeper::RSASHA256 && (bits < 512 || bits > 4096)) {
324 /* RFC5702 */
8f6335a5 325 throw runtime_error(getName() + " RSASHA256 key generation failed for invalid bits size " + std::to_string(bits));
86783480
CH
326 }
327 if (d_algorithm == DNSSECKeeper::RSASHA512 && (bits < 1024 || bits > 4096)) {
328 /* RFC5702 */
8f6335a5 329 throw runtime_error(getName() + " RSASHA512 key generation failed for invalid bits size " + std::to_string(bits));
86783480
CH
330 }
331
eeddacf7 332 auto exponent = BigNum(BN_new(), BN_clear_free);
49faa6a4 333 if (!exponent) {
8f6335a5 334 throw runtime_error(getName() + " key generation failed, unable to allocate e");
a41d80d1
RG
335 }
336
337 /* RSA_F4 is a public exponent value of 65537 */
49faa6a4 338 int res = BN_set_word(exponent.get(), RSA_F4);
a41d80d1
RG
339
340 if (res == 0) {
8f6335a5 341 throw runtime_error(getName() + " key generation failed while setting e");
a41d80d1
RG
342 }
343
eeddacf7
FM
344#if OPENSSL_VERSION_MAJOR >= 3
345 auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
346 if (ctx == nullptr) {
347 throw pdns::OpenSSL::error(getName(), "Could not initialize context");
348 }
349
350 if (EVP_PKEY_keygen_init(ctx.get()) != 1) {
351 throw pdns::OpenSSL::error(getName(), "Could not initialize keygen");
352 }
353
f4bf7b65 354 if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), (int)bits) <= 0) {
eeddacf7
FM
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) {
359 throw pdns::OpenSSL::error(getName(), "Could not set keygen public exponent");
360 }
361
362 EVP_PKEY* key = nullptr;
363 if (EVP_PKEY_generate(ctx.get(), &key) != 1) {
364 throw pdns::OpenSSL::error(getName(), "Could not generate key");
365 }
366
367 d_key.reset(key);
368#else
369 auto key = Key(RSA_new(), RSA_free);
5498c0a9 370 if (!key) {
8f6335a5 371 throw runtime_error(getName() + " allocation of key structure failed");
62be6888
RG
372 }
373
49faa6a4 374 res = RSA_generate_key_ex(key.get(), bits, exponent.get(), nullptr);
a41d80d1 375 if (res == 0) {
8f6335a5 376 throw runtime_error(getName() + " key generation failed");
a41d80d1 377 }
62be6888 378
5498c0a9 379 d_key = std::move(key);
eeddacf7 380#endif
a41d80d1
RG
381}
382
c97af739 383void OpenSSLRSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, const std::optional<std::reference_wrapper<const std::string>> filename)
8f6335a5 384{
b67ad172 385 drc.d_algorithm = d_algorithm;
eeddacf7
FM
386
387#if OPENSSL_VERSION_MAJOR >= 3
388 EVP_PKEY* key = nullptr;
389 if (PEM_read_PrivateKey(&inputFile, &key, nullptr, nullptr) == nullptr) {
c97af739
FM
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");
eeddacf7
FM
395 }
396
397 d_key.reset(key);
398#else
399 d_key = Key(PEM_read_RSAPrivateKey(&inputFile, nullptr, nullptr, nullptr), &RSA_free);
b67ad172 400 if (d_key == nullptr) {
c97af739
FM
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");
b67ad172 406 }
eeddacf7 407#endif
b67ad172
FM
408}
409
a8b5e8ba 410void OpenSSLRSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
8f6335a5 411{
eeddacf7
FM
412#if OPENSSL_VERSION_MAJOR >= 3
413 if (PEM_write_PrivateKey(&outputFile, d_key.get(), nullptr, nullptr, 0, nullptr, nullptr) == 0) {
414 throw pdns::OpenSSL::error(getName(), "Could not convert private key to PEM");
415 }
416#else
49faa6a4 417 auto ret = PEM_write_RSAPrivateKey(&outputFile, d_key.get(), nullptr, nullptr, 0, nullptr, nullptr);
fd421a48
FM
418 if (ret == 0) {
419 throw runtime_error(getName() + ": Could not convert private key to PEM");
420 }
eeddacf7
FM
421#endif
422}
423
424#if OPENSSL_VERSION_MAJOR >= 3
425BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamModulus() const
426{
427 BIGNUM* modulus = nullptr;
428 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_N, &modulus) == 0) {
429 throw pdns::OpenSSL::error(getName(), "Could not get key's modulus (n) parameter");
430 }
431 return BigNum{modulus, BN_clear_free};
432}
433
434BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPublicExponent() const
435{
436 BIGNUM* publicExponent = nullptr;
437 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_E, &publicExponent) == 0) {
438 throw pdns::OpenSSL::error(getName(), "Could not get key's public exponent (e) parameter");
439 }
440 return BigNum{publicExponent, BN_clear_free};
441}
442
443BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrivateExponent() const
444{
445 BIGNUM* privateExponent = nullptr;
446 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_D, &privateExponent) == 0) {
447 throw pdns::OpenSSL::error(getName(), "Could not get key's private exponent (d) parameter");
448 }
449 return BigNum{privateExponent, BN_clear_free};
450}
451
452BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrime1() const
453{
454 BIGNUM* prime1 = nullptr;
455 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_FACTOR1, &prime1) == 0) {
456 throw pdns::OpenSSL::error(getName(), "Could not get key's first prime (p) parameter");
457 }
458 return BigNum{prime1, BN_clear_free};
459}
460
461BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrime2() const
462{
463 BIGNUM* prime2 = nullptr;
464 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_FACTOR2, &prime2) == 0) {
465 throw pdns::OpenSSL::error(getName(), "Could not get key's second prime (q) parameter");
466 }
467 return BigNum{prime2, BN_clear_free};
468}
469
470BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamDmp1() const
471{
472 BIGNUM* dmp1 = nullptr;
473 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_EXPONENT1, &dmp1) == 0) {
474 throw pdns::OpenSSL::error(getName(), "Could not get key's first exponent parameter");
475 }
476 return BigNum{dmp1, BN_clear_free};
477}
478
479BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamDmq1() const
480{
481 BIGNUM* dmq1 = nullptr;
482 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_EXPONENT2, &dmq1) == 0) {
483 throw pdns::OpenSSL::error(getName(), "Could not get key's second exponent parameter");
484 }
485 return BigNum{dmq1, BN_clear_free};
fd421a48 486}
a41d80d1 487
eeddacf7
FM
488BigNum OpenSSLRSADNSCryptoKeyEngine::getKeyParamIqmp() const
489{
490 BIGNUM* iqmp = nullptr;
491 if (EVP_PKEY_get_bn_param(d_key.get(), OSSL_PKEY_PARAM_RSA_COEFFICIENT1, &iqmp) == 0) {
492 throw pdns::OpenSSL::error(getName(), "Could not get key's first coefficient parameter");
493 }
494 return BigNum{iqmp, BN_clear_free};
495}
496#endif
497
498#if OPENSSL_VERSION_MAJOR >= 3
499auto 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{
501 auto params_build = ParamsBuilder(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free);
502 if (params_build == nullptr) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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);
539 if (params == nullptr) {
540 throw pdns::OpenSSL::error(getName(), "Could not create key's parameters");
541 }
542
543 return params;
544}
545#endif
546
a41d80d1
RG
547DNSCryptoKeyEngine::storvector_t OpenSSLRSADNSCryptoKeyEngine::convertToISCVector() const
548{
549 storvector_t storvect;
49faa6a4 550 using outputs_t = vector<pair<string, const BIGNUM*>>;
a41d80d1 551 outputs_t outputs;
eeddacf7
FM
552
553#if OPENSSL_VERSION_MAJOR >= 3
554 // If any of those calls throw, we correctly free the BIGNUMs allocated before it.
555 BigNum modulusPtr = getKeyParamModulus();
556 BigNum publicExponentPtr = getKeyParamPublicExponent();
557 BigNum privateExponentPtr = getKeyParamPrivateExponent();
558 BigNum prime1Ptr = getKeyParamPrime1();
559 BigNum prime2Ptr = getKeyParamPrime2();
560 BigNum dmp1Ptr = getKeyParamDmp1();
561 BigNum dmq1Ptr = getKeyParamDmq1();
562 BigNum iqmpPtr = getKeyParamIqmp();
563
564 // All the calls succeeded, we can take references to the BIGNUM pointers.
565 BIGNUM* modulus = modulusPtr.get();
566 BIGNUM* publicExponent = publicExponentPtr.get();
567 BIGNUM* privateExponent = privateExponentPtr.get();
568 BIGNUM* prime1 = prime1Ptr.get();
569 BIGNUM* prime2 = prime2Ptr.get();
570 BIGNUM* dmp1 = dmp1Ptr.get();
571 BIGNUM* dmq1 = dmq1Ptr.get();
572 BIGNUM* iqmp = iqmpPtr.get();
573#else
49faa6a4
FM
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);
5498c0a9 584 RSA_get0_crt_params(d_key.get(), &dmp1, &dmq1, &iqmp);
eeddacf7
FM
585#endif
586
49faa6a4
FM
587 outputs.emplace_back("Modulus", modulus);
588 outputs.emplace_back("PublicExponent", publicExponent);
589 outputs.emplace_back("PrivateExponent", privateExponent);
590 outputs.emplace_back("Prime1", prime1);
591 outputs.emplace_back("Prime2", prime2);
e32a8d46
RP
592 outputs.emplace_back("Exponent1", dmp1);
593 outputs.emplace_back("Exponent2", dmq1);
594 outputs.emplace_back("Coefficient", iqmp);
a41d80d1 595
8f6335a5
FM
596 string algorithm = std::to_string(d_algorithm);
597 switch (d_algorithm) {
598 case DNSSECKeeper::RSASHA1:
599 case DNSSECKeeper::RSASHA1NSEC3SHA1:
600 algorithm += " (RSASHA1)";
601 break;
602 case DNSSECKeeper::RSASHA256:
603 algorithm += " (RSASHA256)";
604 break;
605 case DNSSECKeeper::RSASHA512:
606 algorithm += " (RSASHA512)";
607 break;
608 default:
609 algorithm += " (?)";
a41d80d1 610 }
e32a8d46 611 storvect.emplace_back("Algorithm", algorithm);
a41d80d1 612
8f6335a5 613 for (const outputs_t::value_type& value : outputs) {
e5f0dee1
RG
614 std::string tmp;
615 tmp.resize(BN_num_bytes(value.second));
eeddacf7
FM
616 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
617 int len = BN_bn2bin(value.second, reinterpret_cast<unsigned char*>(tmp.data()));
e5f0dee1
RG
618 if (len >= 0) {
619 tmp.resize(len);
e32a8d46 620 storvect.emplace_back(value.first, tmp);
e5f0dee1 621 }
a41d80d1
RG
622 }
623
624 return storvect;
625}
626
eeddacf7
FM
627std::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
642const EVP_MD* OpenSSLRSADNSCryptoKeyEngine::hasher() const
643{
644 const EVP_MD* messageDigest = nullptr;
645
646 switch (d_algorithm) {
647 case DNSSECKeeper::RSASHA1:
648 case DNSSECKeeper::RSASHA1NSEC3SHA1:
649 messageDigest = EVP_sha1();
650 break;
651 case DNSSECKeeper::RSASHA256:
652 messageDigest = EVP_sha256();
653 break;
654 case DNSSECKeeper::RSASHA512:
655 messageDigest = EVP_sha512();
656 break;
657 default:
658 throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm));
659 }
660
661 if (messageDigest == nullptr) {
662 throw std::runtime_error("Could not retrieve a SHA implementation of size " + std::to_string(hashSize()) + " from OpenSSL");
663 }
664
665 return messageDigest;
666}
667
49faa6a4 668std::string OpenSSLRSADNSCryptoKeyEngine::hash(const std::string& message) const
a41d80d1 669{
902c4e9c 670 if (d_algorithm == DNSSECKeeper::RSASHA1 || d_algorithm == DNSSECKeeper::RSASHA1NSEC3SHA1) {
49faa6a4
FM
671 std::string l_hash{};
672 l_hash.resize(SHA_DIGEST_LENGTH);
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()));
675 return l_hash;
a41d80d1 676 }
49faa6a4
FM
677
678 if (d_algorithm == DNSSECKeeper::RSASHA256) {
679 std::string l_hash{};
680 l_hash.resize(SHA256_DIGEST_LENGTH);
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()));
683 return l_hash;
a41d80d1 684 }
49faa6a4
FM
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;
a41d80d1
RG
692 }
693
8f6335a5 694 throw runtime_error(getName() + " does not support hash operation for algorithm " + std::to_string(d_algorithm));
a41d80d1
RG
695}
696
349384bf
RG
697int OpenSSLRSADNSCryptoKeyEngine::hashSizeToKind(const size_t hashSize)
698{
44677590
FM
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));
349384bf
RG
710 }
711}
a41d80d1 712
49faa6a4 713std::string OpenSSLRSADNSCryptoKeyEngine::sign(const std::string& message) const
a41d80d1 714{
eeddacf7
FM
715 std::string signature;
716
717#if OPENSSL_VERSION_MAJOR >= 3
718 auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
719 if (ctx == nullptr) {
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) {
724 throw pdns::OpenSSL::error(getName(), "Could not initialize context for signing");
725 }
726
727 std::size_t signatureLen = 0;
728 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
729 const auto* messageData = reinterpret_cast<const unsigned char*>(message.data());
730 if (EVP_DigestSign(ctx.get(), nullptr, &signatureLen, messageData, message.size()) == 0) {
731 throw pdns::OpenSSL::error(getName(), "Could not get message signature length");
732 }
733
734 signature.resize(signatureLen);
735
736 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
737 auto* signatureData = reinterpret_cast<unsigned char*>(signature.data());
738 if (EVP_DigestSign(ctx.get(), signatureData, &signatureLen, messageData, message.size()) == 0) {
739 throw pdns::OpenSSL::error(getName(), "Could not sign message");
740 }
741#else
742 unsigned int signatureLen = 0;
49faa6a4 743 string l_hash = this->hash(message);
41426b6f 744 int hashKind = hashSizeToKind(l_hash.size());
5498c0a9 745 signature.resize(RSA_size(d_key.get()));
a41d80d1 746
eeddacf7 747 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
41426b6f 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());
a41d80d1 749 if (res != 1) {
8f6335a5 750 throw runtime_error(getName() + " failed to generate signature");
a41d80d1
RG
751 }
752
e5f0dee1 753 signature.resize(signatureLen);
eeddacf7
FM
754#endif
755
e5f0dee1 756 return signature;
a41d80d1
RG
757}
758
49faa6a4 759bool OpenSSLRSADNSCryptoKeyEngine::verify(const std::string& message, const std::string& signature) const
a41d80d1 760{
eeddacf7
FM
761#if OPENSSL_VERSION_MAJOR >= 3
762 auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
763 if (ctx == nullptr) {
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) {
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());
773 if (ret < 0) {
774 throw pdns::OpenSSL::error(getName(), "Failed to verify message signature");
775 }
776
777 return (ret == 1);
778#else
49faa6a4 779 string l_hash = this->hash(message);
41426b6f 780 int hashKind = hashSizeToKind(l_hash.size());
a41d80d1 781
41426b6f 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());
a41d80d1
RG
783
784 return (ret == 1);
eeddacf7 785#endif
a41d80d1
RG
786}
787
a41d80d1
RG
788std::string OpenSSLRSADNSCryptoKeyEngine::getPublicKeyString() const
789{
eeddacf7
FM
790#if OPENSSL_VERSION_MAJOR >= 3
791 // If any of those calls throw, we correctly free the BIGNUMs allocated before it.
792 BigNum modulusPtr = getKeyParamModulus();
793 BigNum publicExponentPtr = getKeyParamPublicExponent();
794
795 // All the calls succeeded, we can take references to the BIGNUM pointers.
796 BIGNUM* modulus = modulusPtr.get();
797 BIGNUM* publicExponent = publicExponentPtr.get();
798#else
49faa6a4
FM
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);
eeddacf7
FM
803#endif
804
a41d80d1 805 string keystring;
e5f0dee1 806 std::string tmp;
49faa6a4 807 tmp.resize(std::max(BN_num_bytes(publicExponent), BN_num_bytes(modulus)));
a41d80d1 808
eeddacf7 809 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
49faa6a4 810 int len = BN_bn2bin(publicExponent, reinterpret_cast<unsigned char*>(&tmp.at(0)));
a41d80d1 811 if (len < 255) {
8f6335a5
FM
812 keystring.assign(1, (char)(unsigned int)len);
813 }
814 else {
a41d80d1
RG
815 keystring.assign(1, 0);
816 uint16_t tempLen = len;
817 tempLen = htons(tempLen);
818 keystring.append((char*)&tempLen, 2);
819 }
e5f0dee1 820 keystring.append(&tmp.at(0), len);
a41d80d1 821
eeddacf7 822 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
49faa6a4 823 len = BN_bn2bin(modulus, reinterpret_cast<unsigned char*>(&tmp.at(0)));
e5f0dee1 824 keystring.append(&tmp.at(0), len);
a41d80d1
RG
825
826 return keystring;
827}
828
a41d80d1
RG
829void OpenSSLRSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
830{
eeddacf7
FM
831 auto modulus = mapToBN(getName(), stormap, "modulus");
832 auto publicExponent = mapToBN(getName(), stormap, "publicexponent");
833 auto privateExponent = mapToBN(getName(), stormap, "privateexponent");
888bc297 834
eeddacf7
FM
835 auto prime1 = mapToBN(getName(), stormap, "prime1");
836 auto prime2 = mapToBN(getName(), stormap, "prime2");
888bc297 837
eeddacf7
FM
838 auto dmp1 = mapToBN(getName(), stormap, "exponent1");
839 auto dmq1 = mapToBN(getName(), stormap, "exponent2");
840 auto iqmp = mapToBN(getName(), stormap, "coefficient");
a41d80d1 841
a0383aad 842 pdns::checked_stoi_into(drc.d_algorithm, stormap["algorithm"]);
a41d80d1 843
a41d80d1 844 if (drc.d_algorithm != d_algorithm) {
81901f4a 845 throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc.d_algorithm) + " to a " + std::to_string(d_algorithm) + " key");
a41d80d1 846 }
eeddacf7
FM
847
848#if OPENSSL_VERSION_MAJOR >= 3
849 auto params = makeKeyParams(modulus.get(), publicExponent.get(), privateExponent.get(), prime1.get(), prime2.get(), dmp1.get(), dmq1.get(), iqmp.get());
850
851 auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
852 if (ctx == nullptr) {
853 throw pdns::OpenSSL::error(getName(), "Could not create key context");
854 }
855
856 if (EVP_PKEY_fromdata_init(ctx.get()) <= 0) {
857 throw pdns::OpenSSL::error(getName(), "Could not initialize key context for loading data from ISC");
858 }
859
860 EVP_PKEY* key = nullptr;
861 if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
862 throw pdns::OpenSSL::error(getName(), "Could not create key from parameters");
863 }
864
865 d_key.reset(key);
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
14d02eb2 872 // Everything OK, we're releasing ownership since the RSA_* functions want it
49faa6a4
FM
873 RSA_set0_key(key.get(), modulus.release(), publicExponent.release(), privateExponent.release());
874 RSA_set0_factors(key.get(), prime1.release(), prime2.release());
81901f4a 875 RSA_set0_crt_params(key.get(), dmp1.release(), dmq1.release(), iqmp.release());
a41d80d1 876
5498c0a9 877 d_key = std::move(key);
eeddacf7 878#endif
a41d80d1
RG
879}
880
f7b9d3b7 881bool OpenSSLRSADNSCryptoKeyEngine::checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const
8ca3ea33 882{
ac3f3893
PL
883 bool retval = true;
884 // When changing the bitsizes, also edit them in ::create
8f6335a5 885 if ((d_algorithm == DNSSECKeeper::RSASHA1 || d_algorithm == DNSSECKeeper::RSASHA1NSEC3SHA1 || d_algorithm == DNSSECKeeper::RSASHA256) && (getBits() < 512 || getBits() > 4096)) {
ac3f3893 886 retval = false;
f7b9d3b7
FM
887 if (errorMessages.has_value()) {
888 errorMessages->get().push_back("key is " + std::to_string(getBits()) + " bytes, should be between 512 and 4096");
ac3f3893 889 }
f59c4313
PL
890 }
891 if (d_algorithm == DNSSECKeeper::RSASHA512 && (getBits() < 1024 || getBits() > 4096)) {
ac3f3893 892 retval = false;
f7b9d3b7
FM
893 if (errorMessages.has_value()) {
894 errorMessages->get().push_back("key is " + std::to_string(getBits()) + " bytes, should be between 1024 and 4096");
ac3f3893 895 }
f59c4313 896 }
eeddacf7
FM
897
898#if OPENSSL_VERSION_MAJOR >= 3
899 auto ctx = KeyContext(EVP_PKEY_CTX_new_from_pkey(nullptr, d_key.get(), nullptr), EVP_PKEY_CTX_free);
900 if (ctx == nullptr) {
901 throw pdns::OpenSSL::error(getName(), "Cannot create context to check key");
902 }
903
904 if (EVP_PKEY_pairwise_check(ctx.get()) != 1) {
905#else
128bc6d2 906 if (RSA_check_key(d_key.get()) != 1) {
eeddacf7 907#endif
ac3f3893 908 retval = false;
f7b9d3b7 909 if (errorMessages.has_value()) {
eeddacf7 910 const auto* errmsg = ERR_error_string(ERR_get_error(), nullptr);
843bc504
PD
911 if (errmsg == nullptr) {
912 errmsg = "Unknown OpenSSL error";
913 }
f7b9d3b7 914 errorMessages->get().emplace_back(errmsg);
ac3f3893
PL
915 }
916 }
917 return retval;
8ca3ea33
RG
918}
919
49faa6a4 920void OpenSSLRSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& content)
a41d80d1 921{
eeddacf7 922 string publicExponent;
49faa6a4
FM
923 string modulus;
924 const size_t contentLen = content.length();
eeddacf7
FM
925
926 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
927 const auto* raw = reinterpret_cast<const unsigned char*>(content.c_str());
a41d80d1 928
49faa6a4 929 if (contentLen < 1) {
8f6335a5 930 throw runtime_error(getName() + " invalid input size for the public key");
349384bf
RG
931 }
932
a41d80d1 933 if (raw[0] != 0) {
349384bf 934 const size_t exponentSize = raw[0];
49faa6a4 935 if (contentLen < (exponentSize + 2)) {
8f6335a5 936 throw runtime_error(getName() + " invalid input size for the public key");
349384bf 937 }
eeddacf7 938 publicExponent = content.substr(1, exponentSize);
49faa6a4 939 modulus = content.substr(exponentSize + 1);
8f6335a5
FM
940 }
941 else {
49faa6a4 942 if (contentLen < 3) {
8f6335a5 943 throw runtime_error(getName() + " invalid input size for the public key");
349384bf 944 }
8f6335a5 945 const size_t exponentSize = raw[1] * 0xff + raw[2];
49faa6a4 946 if (contentLen < (exponentSize + 4)) {
8f6335a5 947 throw runtime_error(getName() + " invalid input size for the public key");
349384bf 948 }
eeddacf7 949 publicExponent = content.substr(3, exponentSize);
49faa6a4 950 modulus = content.substr(exponentSize + 3);
a41d80d1
RG
951 }
952
eeddacf7
FM
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);
955 if (!publicExponentBN) {
8f6335a5 956 throw runtime_error(getName() + " error loading public exponent (e) value of public key");
a41d80d1 957 }
8f6335a5 958
eeddacf7
FM
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);
961 if (!modulusBN) {
8f6335a5 962 throw runtime_error(getName() + " error loading modulus (n) value of public key");
a41d80d1 963 }
8ca3ea33 964
eeddacf7
FM
965#if OPENSSL_VERSION_MAJOR >= 3
966 auto params = makeKeyParams(modulusBN.get(), publicExponentBN.get(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
967
968 auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
969 if (ctx == nullptr) {
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) {
974 throw pdns::OpenSSL::error(getName(), "Could not initialize key context for loading data to check key");
975 }
976
977 EVP_PKEY* key = nullptr;
978 if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_PUBLIC_KEY, params.get()) <= 0) {
979 throw pdns::OpenSSL::error(getName(), "Could not create public key from parameters");
980 }
981
982 d_key.reset(key);
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);
5498c0a9 990 d_key = std::move(key);
eeddacf7 991#endif
a41d80d1 992}
7581a35b 993
74d83458 994#ifdef HAVE_LIBCRYPTO_ECDSA
7581a35b
KM
995class OpenSSLECDSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
996{
997public:
1b8b729a 998 explicit OpenSSLECDSADNSCryptoKeyEngine(unsigned int algo);
7581a35b 999
1b8b729a 1000 [[nodiscard]] string getName() const override { return "OpenSSL ECDSA"; }
f5b73a82 1001 [[nodiscard]] int getBits() const override;
7581a35b 1002
d12b259b 1003 void create(unsigned int bits) override;
87066241
FM
1004
1005 /**
1006 * \brief Creates an ECDSA key engine from a PEM file.
1007 *
a538f953 1008 * Receives an open file handle with PEM contents and creates an ECDSA key engine.
87066241
FM
1009 *
1010 * \param[in] drc Key record contents to be populated.
1011 *
a538f953 1012 * \param[in] inputFile An open file handle to a file containing ECDSA PEM contents.
87066241 1013 *
a538f953 1014 * \param[in] filename Only used for providing filename information in error messages.
87066241 1015 *
a538f953 1016 * \return An ECDSA key engine populated with the contents of the PEM file.
87066241 1017 */
c97af739 1018 void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
87066241 1019
18567bc6
FM
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 *
1b8b729a 1026 * \param[in] outputFile An open file handle for writing.
18567bc6
FM
1027 *
1028 * \exception std::runtime_error In case of OpenSSL errors.
1029 */
a8b5e8ba 1030 void convertToPEMFile(std::FILE& outputFile) const override;
18567bc6 1031
1b8b729a
FM
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;
d12b259b
CH
1037 void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
1038 void fromPublicKeyString(const std::string& content) override;
f7b9d3b7 1039 [[nodiscard]] bool checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const override;
7581a35b 1040
f5b73a82
FM
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
a2c6e554 1046 static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
7581a35b 1047 {
a2c6e554 1048 return make_unique<OpenSSLECDSADNSCryptoKeyEngine>(algorithm);
7581a35b
KM
1049 }
1050
1051private:
f5b73a82
FM
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)>;
87804a9c 1062 using MessageDigestContext = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>;
f5b73a82 1063#else
1b8b729a 1064 using Key = std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)>;
f5b73a82
FM
1065#endif
1066
1067 using KeyContext = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>;
1b8b729a
FM
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
f5b73a82
FM
1072 int d_len{0};
1073 std::string d_group_name{};
1074 Group d_group{nullptr, EC_GROUP_free};
7581a35b 1075
f5b73a82
FM
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
7581a35b
KM
1081};
1082
f5b73a82
FM
1083int OpenSSLECDSADNSCryptoKeyEngine::getBits() const
1084{
1085 return d_len << 3;
1086}
1087
1b8b729a 1088OpenSSLECDSADNSCryptoKeyEngine::OpenSSLECDSADNSCryptoKeyEngine(unsigned int algo) :
f5b73a82
FM
1089 DNSCryptoKeyEngine(algo)
1090#if OPENSSL_VERSION_MAJOR < 3
1091 ,
1092 d_eckey(Key(EC_KEY_new(), EC_KEY_free))
1093#endif
1b8b729a
FM
1094{
1095 int ret = RAND_status();
1096 if (ret != 1) {
1097 throw runtime_error(getName() + " insufficient entropy");
1098 }
1099
f5b73a82 1100#if OPENSSL_VERSION_MAJOR < 3
1b8b729a
FM
1101 if (!d_eckey) {
1102 throw runtime_error(getName() + " allocation of key structure failed");
1103 }
f5b73a82
FM
1104#endif
1105
1106 int d_id{0};
1b8b729a
FM
1107
1108 if (d_algorithm == 13) {
f5b73a82 1109 d_group_name = "P-256";
1b8b729a 1110 d_len = 32;
f5b73a82 1111 d_id = NID_X9_62_prime256v1;
1b8b729a
FM
1112 }
1113 else if (d_algorithm == 14) {
f5b73a82 1114 d_group_name = "P-384";
1b8b729a 1115 d_len = 48;
f5b73a82 1116 d_id = NID_secp384r1;
1b8b729a
FM
1117 }
1118 else {
1119 throw runtime_error(getName() + " unknown algorithm " + std::to_string(d_algorithm));
1120 }
1121
f5b73a82
FM
1122 d_group = Group(EC_GROUP_new_by_curve_name(d_id), EC_GROUP_free);
1123 if (d_group == nullptr) {
1124 throw pdns::OpenSSL::error(getName(), std::string() + "Failed to create EC group `" + d_group_name + "` to export public key");
1b8b729a
FM
1125 }
1126
f5b73a82
FM
1127#if OPENSSL_VERSION_MAJOR < 3
1128 ret = EC_KEY_set_group(d_eckey.get(), d_group.get());
1b8b729a
FM
1129 if (ret != 1) {
1130 throw runtime_error(getName() + " setting key group failed");
1131 }
f5b73a82 1132#endif
1b8b729a
FM
1133}
1134
7581a35b
KM
1135void OpenSSLECDSADNSCryptoKeyEngine::create(unsigned int bits)
1136{
f5b73a82 1137 if (bits >> 3 != static_cast<unsigned int>(d_len)) {
37fa09a3 1138 throw runtime_error(getName() + " unknown key length of " + std::to_string(bits) + " bits requested");
7581a35b
KM
1139 }
1140
f5b73a82
FM
1141#if OPENSSL_VERSION_MAJOR >= 3
1142 // NOLINTNEXTLINE(*-vararg): Using OpenSSL C APIs.
1143 EVP_PKEY* key = EVP_PKEY_Q_keygen(nullptr, nullptr, "EC", d_group_name.c_str());
1144 if (key == nullptr) {
1145 throw pdns::OpenSSL::error(getName(), "Failed to generate key");
1146 }
1147
1148 d_eckey.reset(key);
1149#else
5498c0a9 1150 int res = EC_KEY_generate_key(d_eckey.get());
7581a35b 1151 if (res == 0) {
37fa09a3 1152 throw runtime_error(getName() + " key generation failed");
7581a35b 1153 }
061c5e88
FM
1154
1155 EC_KEY_set_asn1_flag(d_eckey.get(), OPENSSL_EC_NAMED_CURVE);
f5b73a82 1156#endif
7581a35b
KM
1157}
1158
c97af739 1159void OpenSSLECDSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename)
87066241
FM
1160{
1161 drc.d_algorithm = d_algorithm;
f5b73a82
FM
1162
1163#if OPENSSL_VERSION_MAJOR >= 3
1164 EVP_PKEY* key = nullptr;
1165 if (PEM_read_PrivateKey(&inputFile, &key, nullptr, nullptr) == nullptr) {
c97af739
FM
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");
f5b73a82
FM
1171 }
1172
1173 d_eckey.reset(key);
1174#else
1b8b729a 1175 d_eckey = Key(PEM_read_ECPrivateKey(&inputFile, nullptr, nullptr, nullptr), &EC_KEY_free);
87066241 1176 if (d_eckey == nullptr) {
c97af739
FM
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");
87066241
FM
1182 }
1183
f5b73a82 1184 int ret = EC_KEY_set_group(d_eckey.get(), d_group.get());
87066241
FM
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
f5b73a82 1191 auto pub_key = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
87066241
FM
1192 if (!pub_key) {
1193 throw runtime_error(getName() + " allocation of public key point failed");
1194 }
1195
f5b73a82 1196 ret = EC_POINT_mul(d_group.get(), pub_key.get(), privateKeyBN, nullptr, nullptr, nullptr);
87066241
FM
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);
f5b73a82 1208#endif
87066241 1209}
7581a35b 1210
a8b5e8ba 1211void OpenSSLECDSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
18567bc6 1212{
f5b73a82
FM
1213#if OPENSSL_VERSION_MAJOR >= 3
1214 if (PEM_write_PrivateKey(&outputFile, d_eckey.get(), nullptr, nullptr, 0, nullptr, nullptr) == 0) {
1215 throw pdns::OpenSSL::error(getName(), "Failed to convert private key to PEM");
1216 }
1217#else
1b8b729a 1218 auto ret = PEM_write_ECPrivateKey(&outputFile, d_eckey.get(), nullptr, nullptr, 0, nullptr, nullptr);
18567bc6
FM
1219 if (ret == 0) {
1220 throw runtime_error(getName() + ": Could not convert private key to PEM");
1221 }
f5b73a82
FM
1222#endif
1223}
1224
1225#if OPENSSL_VERSION_MAJOR >= 3
1226auto OpenSSLECDSADNSCryptoKeyEngine::getPrivateKey() const -> BigNum
1227{
1228 BIGNUM* privateKey = nullptr;
1229 if (EVP_PKEY_get_bn_param(d_eckey.get(), OSSL_PKEY_PARAM_PRIV_KEY, &privateKey) == 0) {
1230 throw pdns::OpenSSL::error(getName(), "Could not get private key parameter");
1231 }
1232 return BigNum{privateKey, BN_clear_free};
18567bc6 1233}
f5b73a82 1234#endif
18567bc6 1235
7581a35b
KM
1236DNSCryptoKeyEngine::storvector_t OpenSSLECDSADNSCryptoKeyEngine::convertToISCVector() const
1237{
1238 storvector_t storvect;
1239 string algorithm;
1240
1b8b729a 1241 if (d_algorithm == 13) {
7581a35b 1242 algorithm = "13 (ECDSAP256SHA256)";
1b8b729a
FM
1243 }
1244 else if (d_algorithm == 14) {
7581a35b 1245 algorithm = "14 (ECDSAP384SHA384)";
1b8b729a
FM
1246 }
1247 else {
7581a35b 1248 algorithm = " ? (?)";
1b8b729a 1249 }
7581a35b 1250
e32a8d46 1251 storvect.emplace_back("Algorithm", algorithm);
7581a35b 1252
f5b73a82
FM
1253#if OPENSSL_VERSION_MAJOR >= 3
1254 auto privateKeyBN = getPrivateKey();
1255
1256 std::string privateKey;
1257 privateKey.resize(BN_num_bytes(privateKeyBN.get()));
1258 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1259 int len = BN_bn2bin(privateKeyBN.get(), reinterpret_cast<unsigned char*>(privateKey.data()));
1260 if (len >= 0) {
1261 privateKey.resize(len);
1262
1263 std::string prefix;
1264 if (d_len - len != 0) {
1265 prefix.append(d_len - len, 0x00);
1266 }
1267
1268 storvect.emplace_back("PrivateKey", prefix + privateKey);
1269 }
1270#else
37fa09a3 1271 const BIGNUM* key = EC_KEY_get0_private_key(d_eckey.get());
5498c0a9 1272 if (key == nullptr) {
37fa09a3 1273 throw runtime_error(getName() + " private key not set");
7581a35b
KM
1274 }
1275
e5f0dee1
RG
1276 std::string tmp;
1277 tmp.resize(BN_num_bytes(key));
1278 int len = BN_bn2bin(key, reinterpret_cast<unsigned char*>(&tmp.at(0)));
7581a35b
KM
1279
1280 string prefix;
1b8b729a 1281 if (d_len - len) {
7581a35b 1282 prefix.append(d_len - len, 0x00);
1b8b729a 1283 }
7581a35b 1284
e32a8d46 1285 storvect.emplace_back("PrivateKey", prefix + tmp);
f5b73a82 1286#endif
7581a35b
KM
1287
1288 return storvect;
1289}
1290
1b8b729a 1291std::string OpenSSLECDSADNSCryptoKeyEngine::hash(const std::string& message) const
7581a35b 1292{
37fa09a3 1293 if (getBits() == 256) {
1b8b729a
FM
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;
7581a35b 1299 }
1b8b729a
FM
1300
1301 if (getBits() == 384) {
1302 std::string l_hash{};
1303 l_hash.resize(SHA384_DIGEST_LENGTH);
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()));
1306 return l_hash;
7581a35b
KM
1307 }
1308
37fa09a3 1309 throw runtime_error(getName() + " does not support a hash size of " + std::to_string(getBits()) + " bits");
7581a35b
KM
1310}
1311
f5b73a82
FM
1312const EVP_MD* OpenSSLECDSADNSCryptoKeyEngine::hasher() const
1313{
1314 const EVP_MD* messageDigest = nullptr;
1315
1316 switch (d_algorithm) {
1317 case DNSSECKeeper::ECDSA256:
1318 messageDigest = EVP_sha256();
1319 break;
1320 case DNSSECKeeper::ECDSA384:
1321 messageDigest = EVP_sha384();
1322 break;
1323 default:
1324 throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm));
1325 }
1326
1327 if (messageDigest == nullptr) {
1328 throw std::runtime_error("Could not retrieve a SHA implementation of size " + std::to_string(hashSize()) + " from OpenSSL");
1329 }
1330
1331 return messageDigest;
1332}
1333
1334std::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
1b8b729a 1346std::string OpenSSLECDSADNSCryptoKeyEngine::sign(const std::string& message) const
7581a35b 1347{
f5b73a82
FM
1348#if OPENSSL_VERSION_MAJOR >= 3
1349 auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1350 if (ctx == nullptr) {
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) {
1355 throw pdns::OpenSSL::error(getName(), "Could not initialize context for signing");
1356 }
1357
1358 std::size_t signatureLen = 0;
1359
1360 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1361 const auto* messageData = reinterpret_cast<const unsigned char*>(message.data());
1362 if (EVP_DigestSign(ctx.get(), nullptr, &signatureLen, messageData, message.size()) == 0) {
1363 throw pdns::OpenSSL::error(getName(), "Could not get message signature length");
1364 }
1365
1366 std::string signatureBuffer;
1367 signatureBuffer.resize(signatureLen);
1368
1369 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1370 auto* signatureData = reinterpret_cast<unsigned char*>(signatureBuffer.data());
1371 if (EVP_DigestSign(ctx.get(), signatureData, &signatureLen, messageData, message.size()) == 0) {
1372 throw pdns::OpenSSL::error(getName(), "Could not sign message");
1373 }
1374
1375 signatureBuffer.resize(signatureLen);
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);
1379 if (signature == nullptr) {
1380 throw pdns::OpenSSL::error(getName(), "Failed to convert DER signature to internal structure");
1381 }
1382#else
1b8b729a 1383 string l_hash = this->hash(message);
7581a35b 1384
1b8b729a 1385 auto signature = Signature(ECDSA_do_sign((unsigned char*)l_hash.c_str(), l_hash.length(), d_eckey.get()), ECDSA_SIG_free);
5498c0a9 1386 if (!signature) {
37fa09a3 1387 throw runtime_error(getName() + " failed to generate signature");
7581a35b 1388 }
f5b73a82 1389#endif
7581a35b
KM
1390
1391 string ret;
e5f0dee1
RG
1392 std::string tmp;
1393 tmp.resize(d_len);
7581a35b 1394
f5b73a82
FM
1395 const BIGNUM* prComponent = nullptr;
1396 const BIGNUM* psComponent = nullptr;
1397 ECDSA_SIG_get0(signature.get(), &prComponent, &psComponent);
1398 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1399 int len = BN_bn2bin(prComponent, reinterpret_cast<unsigned char*>(&tmp.at(0)));
1400 if ((d_len - len) != 0) {
7581a35b 1401 ret.append(d_len - len, 0x00);
1b8b729a 1402 }
e5f0dee1 1403 ret.append(&tmp.at(0), len);
7581a35b 1404
f5b73a82
FM
1405 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1406 len = BN_bn2bin(psComponent, reinterpret_cast<unsigned char*>(&tmp.at(0)));
1407 if ((d_len - len) != 0) {
7581a35b 1408 ret.append(d_len - len, 0x00);
1b8b729a 1409 }
e5f0dee1 1410 ret.append(&tmp.at(0), len);
7581a35b 1411
7581a35b
KM
1412 return ret;
1413}
1414
1b8b729a 1415bool OpenSSLECDSADNSCryptoKeyEngine::verify(const std::string& message, const std::string& signature) const
7581a35b 1416{
f5b73a82 1417 if (signature.length() != (static_cast<unsigned long>(d_len) * 2)) {
37fa09a3 1418 throw runtime_error(getName() + " invalid signature size " + std::to_string(signature.length()));
7581a35b
KM
1419 }
1420
f5b73a82
FM
1421 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1422 auto* signatureCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(signature.c_str()));
1423 auto rComponent = BigNum(BN_bin2bn(signatureCStr, d_len, nullptr), BN_free);
1424 auto sComponent = BigNum(BN_bin2bn(signatureCStr + d_len, d_len, nullptr), BN_free);
1425 if (!rComponent || !sComponent) {
1426 throw runtime_error(getName() + " invalid signature");
1427 }
7581a35b 1428
1b8b729a 1429 auto sig = Signature(ECDSA_SIG_new(), ECDSA_SIG_free);
5498c0a9 1430 if (!sig) {
37fa09a3 1431 throw runtime_error(getName() + " allocation of signature structure failed");
7581a35b 1432 }
f5b73a82 1433 ECDSA_SIG_set0(sig.get(), rComponent.release(), sComponent.release());
7581a35b 1434
f5b73a82
FM
1435#if OPENSSL_VERSION_MAJOR >= 3
1436 unsigned char* derBufferPointer = nullptr;
1437 const int derBufferSize = i2d_ECDSA_SIG(sig.get(), &derBufferPointer);
1438 if (derBufferSize < 0) {
1439 throw pdns::OpenSSL::error(getName(), "Failed to convert signature to DER");
7581a35b 1440 }
f5b73a82
FM
1441 // Because OPENSSL_free() is a macro.
1442 auto derBuffer = unique_ptr<unsigned char, void (*)(unsigned char*)>{derBufferPointer, [](auto* buffer) { OPENSSL_free(buffer); }};
7581a35b 1443
f5b73a82
FM
1444 auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1445 if (ctx == nullptr) {
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) {
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());
1455 if (ret < 0) {
1456 throw pdns::OpenSSL::error(getName(), "Could not verify message signature");
1457 }
7581a35b 1458
f5b73a82
FM
1459 return (ret == 1);
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());
37fa09a3
FM
1464 if (ret == -1) {
1465 throw runtime_error(getName() + " verify error");
7581a35b
KM
1466 }
1467
1468 return (ret == 1);
f5b73a82 1469#endif
7581a35b
KM
1470}
1471
7581a35b
KM
1472std::string OpenSSLECDSADNSCryptoKeyEngine::getPublicKeyString() const
1473{
f5b73a82
FM
1474#if OPENSSL_VERSION_MAJOR >= 3
1475 size_t bufsize = 0;
1476 if (EVP_PKEY_get_octet_string_param(d_eckey.get(), OSSL_PKEY_PARAM_PUB_KEY, nullptr, 0, &bufsize) == 0) {
1477 throw pdns::OpenSSL::error(getName(), "Failed to get public key buffer size");
1478 }
1479
1480 std::string publicKey{};
1481 publicKey.resize(bufsize);
1482
1483 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1484 auto* publicKeyCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(publicKey.c_str()));
1485 if (EVP_PKEY_get_octet_string_param(d_eckey.get(), OSSL_PKEY_PARAM_PUB_KEY, publicKeyCStr, bufsize, &bufsize) == 0) {
1486 throw pdns::OpenSSL::error(getName(), "Failed to get public key");
1487 }
1488
1489 publicKey.resize(bufsize);
1490
1491 auto publicKeyECPoint = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
1492 if (publicKeyECPoint == nullptr) {
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);
1497
1498 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1499 publicKeyCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(publicKey.c_str()));
1500 if (EC_POINT_oct2point(d_group.get(), publicKeyECPoint.get(), publicKeyCStr, publicKey.length(), ctx.get()) == 0) {
1501 throw pdns::OpenSSL::error(getName(), "Failed to export public key to point");
1502 }
1503
1504 std::string publicKeyUncompressed{};
1505 bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
1506 if (bufsize == 0) {
1507 throw pdns::OpenSSL::error(getName(), "Failed to get public key binary buffer size");
1508 }
1509 publicKeyUncompressed.resize(bufsize);
1510
1511 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1512 auto* publicKeyUncompressedCStr = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(publicKeyUncompressed.c_str()));
1513 bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_UNCOMPRESSED, publicKeyUncompressedCStr, publicKeyUncompressed.length(), nullptr);
1514 if (bufsize == 0) {
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);
1522
1523 return publicKeyUncompressed;
1524#else
e5f0dee1
RG
1525 std::string binaryPoint;
1526 binaryPoint.resize((d_len * 2) + 1);
7581a35b 1527
f5b73a82 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);
7581a35b 1529 if (ret == 0) {
37fa09a3 1530 throw runtime_error(getName() + " exporting point to binary failed");
7581a35b
KM
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" */
e5f0dee1
RG
1536 binaryPoint.erase(0, 1);
1537 return binaryPoint;
f5b73a82
FM
1538#endif
1539}
1540
1541#if OPENSSL_VERSION_MAJOR >= 3
1542auto OpenSSLECDSADNSCryptoKeyEngine::makeKeyParams(const std::string& group_name, const BIGNUM* privateKey, const std::optional<std::string>& publicKey) const -> Params
1543{
1544 auto params_build = ParamsBuilder(OSSL_PARAM_BLD_new(), OSSL_PARAM_BLD_free);
1545 if (params_build == nullptr) {
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) {
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) {
1554 throw pdns::OpenSSL::error(getName(), "Failed to create private key parameter");
1555 }
1556
1557 if (publicKey.has_value()) {
1558 if (OSSL_PARAM_BLD_push_octet_string(params_build.get(), OSSL_PKEY_PARAM_PUB_KEY, publicKey->c_str(), publicKey->length()) == 0) {
1559 throw pdns::OpenSSL::error(getName(), "Failed to create public key parameter");
1560 }
1561 }
1562
1563 auto params = Params(OSSL_PARAM_BLD_to_param(params_build.get()), OSSL_PARAM_free);
1564 if (params == nullptr) {
1565 throw pdns::OpenSSL::error(getName(), "Failed to create key's parameters");
1566 }
1567
1568 return params;
7581a35b 1569}
f5b73a82 1570#endif
7581a35b 1571
7581a35b
KM
1572void OpenSSLECDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
1573{
1574 drc.d_algorithm = atoi(stormap["algorithm"].c_str());
1575
1576 if (drc.d_algorithm != d_algorithm) {
37fa09a3 1577 throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc.d_algorithm) + " to a " + std::to_string(d_algorithm) + " key");
7581a35b
KM
1578 }
1579
f5b73a82
FM
1580 auto privateKey = mapToBN(getName(), stormap, "privatekey");
1581
1582#if OPENSSL_VERSION_MAJOR >= 3
1583 auto publicKeyECPoint = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
1584 if (publicKeyECPoint == nullptr) {
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) {
1589 throw pdns::OpenSSL::error(getName(), "Failed to derive public key from ISC private key");
1590 }
1591
1592 std::string publicKey{};
1593 size_t bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_COMPRESSED, nullptr, 0, nullptr);
1594 if (bufsize == 0) {
1595 throw pdns::OpenSSL::error(getName(), "Failed to get public key binary buffer size");
1596 }
1597 publicKey.resize(bufsize);
1598
1599 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1600 auto* publicKeyData = reinterpret_cast<unsigned char*>(publicKey.data());
1601 bufsize = EC_POINT_point2oct(d_group.get(), publicKeyECPoint.get(), POINT_CONVERSION_COMPRESSED, publicKeyData, publicKey.length(), nullptr);
1602 if (bufsize == 0) {
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));
1607
1608 auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free);
1609 if (ctx == nullptr) {
1610 throw pdns::OpenSSL::error(getName(), "Could not create key context");
1611 }
7581a35b 1612
f5b73a82
FM
1613 if (EVP_PKEY_fromdata_init(ctx.get()) <= 0) {
1614 throw pdns::OpenSSL::error(getName(), "Could not initialize key context for loading data from ISC");
7581a35b
KM
1615 }
1616
f5b73a82
FM
1617 EVP_PKEY* key = nullptr;
1618 if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
1619 throw pdns::OpenSSL::error(getName(), "Could not create key from parameters");
1620 }
1621
1622 d_eckey.reset(key);
1623#else
1624 int ret = EC_KEY_set_private_key(d_eckey.get(), privateKey.get());
7581a35b 1625 if (ret != 1) {
37fa09a3 1626 throw runtime_error(getName() + " setting private key failed");
7581a35b
KM
1627 }
1628
f5b73a82 1629 auto pub_key = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
5498c0a9 1630 if (!pub_key) {
37fa09a3 1631 throw runtime_error(getName() + " allocation of public key point failed");
7581a35b
KM
1632 }
1633
f5b73a82 1634 ret = EC_POINT_mul(d_group.get(), pub_key.get(), privateKey.get(), nullptr, nullptr, nullptr);
7581a35b 1635 if (ret != 1) {
37fa09a3 1636 throw runtime_error(getName() + " computing public key from private failed");
7581a35b
KM
1637 }
1638
5498c0a9 1639 ret = EC_KEY_set_public_key(d_eckey.get(), pub_key.get());
7581a35b 1640 if (ret != 1) {
37fa09a3 1641 throw runtime_error(getName() + " setting public key failed");
7581a35b 1642 }
061c5e88
FM
1643
1644 EC_KEY_set_asn1_flag(d_eckey.get(), OPENSSL_EC_NAMED_CURVE);
f5b73a82 1645#endif
7581a35b
KM
1646}
1647
f7b9d3b7 1648bool OpenSSLECDSADNSCryptoKeyEngine::checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const
8ca3ea33 1649{
f5b73a82
FM
1650#if OPENSSL_VERSION_MAJOR >= 3
1651 auto ctx = KeyContext{EVP_PKEY_CTX_new_from_pkey(nullptr, d_eckey.get(), nullptr), EVP_PKEY_CTX_free};
1652 if (ctx == nullptr) {
1653 throw pdns::OpenSSL::error(getName(), "Failed to create context to check key");
1654 }
1655
1656 bool retval = true;
1657
1658 auto addOpenSSLErrorMessageOnFail = [errorMessages, &retval](const int errorCode, const auto defaultErrorMessage) {
1659 // Error code of -2 means the check is not supported for the algorithm, which is fine.
1660 if (errorCode != 1 && errorCode != -2) {
1661 retval = false;
1662
f7b9d3b7 1663 if (errorMessages.has_value()) {
f5b73a82
FM
1664 const auto* errorMessage = ERR_reason_error_string(ERR_get_error());
1665 if (errorMessage == nullptr) {
f7b9d3b7 1666 errorMessages->get().push_back(defaultErrorMessage);
f5b73a82
FM
1667 }
1668 else {
f7b9d3b7 1669 errorMessages->get().emplace_back(errorMessage);
f5b73a82
FM
1670 }
1671 }
1672 }
1673 };
1674
1675 addOpenSSLErrorMessageOnFail(EVP_PKEY_param_check(ctx.get()), getName() + "Unknown OpenSSL error during key param check");
1676 addOpenSSLErrorMessageOnFail(EVP_PKEY_public_check(ctx.get()), getName() + "Unknown OpenSSL error during public key check");
1677 addOpenSSLErrorMessageOnFail(EVP_PKEY_private_check(ctx.get()), getName() + "Unknown OpenSSL error during private key check");
1678 addOpenSSLErrorMessageOnFail(EVP_PKEY_pairwise_check(ctx.get()), getName() + "Unknown OpenSSL error during key pairwise check");
1679
1680 return retval;
1681#else
ac3f3893 1682 bool retval = true;
128bc6d2 1683 if (EC_KEY_check_key(d_eckey.get()) != 1) {
ac3f3893 1684 retval = false;
f7b9d3b7 1685 if (errorMessages.has_value()) {
1b8b729a 1686 const auto* errmsg = ERR_reason_error_string(ERR_get_error());
843bc504
PD
1687 if (errmsg == nullptr) {
1688 errmsg = "Unknown OpenSSL error";
1689 }
f7b9d3b7 1690 errorMessages->get().push_back(errmsg);
ac3f3893
PL
1691 }
1692 }
1693 return retval;
f5b73a82 1694#endif
8ca3ea33 1695}
7581a35b 1696
1b8b729a 1697void OpenSSLECDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& content)
7581a35b 1698{
f5b73a82
FM
1699#if OPENSSL_VERSION_MAJOR >= 3
1700 /* uncompressed point, from SEC1: "2.3.4 Octet-String-to-Elliptic-Curve-Point
1701 * Conversion"
1702 */
1703 std::string publicKey = "\x04";
1704 publicKey.append(content);
1705
1706 auto params = makeKeyParams(d_group_name, nullptr, std::make_optional(publicKey));
1707
1708 auto ctx = KeyContext(EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free);
1709 if (ctx == nullptr) {
1710 throw pdns::OpenSSL::error(getName(), "Failed to create key context");
1711 }
1712
1713 if (EVP_PKEY_fromdata_init(ctx.get()) <= 0) {
1714 throw pdns::OpenSSL::error(getName(), "Failed to initialize key context for loading data from ISC");
1715 }
1716
1717 EVP_PKEY* key = nullptr;
1718 if (EVP_PKEY_fromdata(ctx.get(), &key, EVP_PKEY_PUBLIC_KEY, params.get()) <= 0) {
1719 throw pdns::OpenSSL::error(getName(), "Failed to create key from parameters");
1720 }
1721
1722 d_eckey.reset(key);
1723#else
1724 /* uncompressed point, from SEC1: "2.3.4 Octet-String-to-Elliptic-Curve-Point
1725 * Conversion"
1726 */
1b8b729a
FM
1727 string ecdsaPoint = "\x04";
1728 ecdsaPoint.append(content);
7581a35b 1729
f5b73a82 1730 auto pub_key = Point(EC_POINT_new(d_group.get()), EC_POINT_free);
5498c0a9 1731 if (!pub_key) {
37fa09a3 1732 throw runtime_error(getName() + " allocation of point structure failed");
7581a35b
KM
1733 }
1734
f5b73a82 1735 int ret = EC_POINT_oct2point(d_group.get(), pub_key.get(), (unsigned char*)ecdsaPoint.c_str(), ecdsaPoint.length(), nullptr);
7581a35b 1736 if (ret != 1) {
37fa09a3 1737 throw runtime_error(getName() + " reading ECP point from binary failed");
7581a35b
KM
1738 }
1739
5498c0a9 1740 ret = EC_KEY_set_private_key(d_eckey.get(), nullptr);
7581a35b 1741 if (ret == 1) {
37fa09a3 1742 throw runtime_error(getName() + " setting private key failed");
7581a35b
KM
1743 }
1744
5498c0a9 1745 ret = EC_KEY_set_public_key(d_eckey.get(), pub_key.get());
7581a35b 1746 if (ret != 1) {
37fa09a3 1747 throw runtime_error(getName() + " setting public key failed");
7581a35b 1748 }
f5b73a82 1749#endif
7581a35b 1750}
de4e1d0d 1751#endif
7581a35b 1752
439974d7
PL
1753#ifdef HAVE_LIBCRYPTO_EDDSA
1754class OpenSSLEDDSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
1755{
1756public:
80e3989f 1757 explicit OpenSSLEDDSADNSCryptoKeyEngine(unsigned int algo);
439974d7 1758
80e3989f
FM
1759 [[nodiscard]] string getName() const override { return "OpenSSL EdDSA"; }
1760 [[nodiscard]] int getBits() const override;
439974d7
PL
1761
1762 void create(unsigned int bits) override;
77183aa7 1763
b14ffbb0
FM
1764 /**
1765 * \brief Creates an EDDSA key engine from a PEM file.
1766 *
a538f953 1767 * Receives an open file handle with PEM contents and creates an EDDSA key engine.
b14ffbb0
FM
1768 *
1769 * \param[in] drc Key record contents to be populated.
1770 *
a538f953 1771 * \param[in] inputFile An open file handle to a file containing EDDSA PEM contents.
b14ffbb0 1772 *
a538f953 1773 * \param[in] filename Only used for providing filename information in error messages.
b14ffbb0 1774 *
a538f953 1775 * \return An EDDSA key engine populated with the contents of the PEM file.
b14ffbb0 1776 */
c97af739 1777 void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
b14ffbb0 1778
77183aa7
FM
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 *
6d40ba53 1785 * \param[in] outputFile An open file handle for writing.
77183aa7
FM
1786 *
1787 * \exception std::runtime_error In case of OpenSSL errors.
1788 */
a8b5e8ba 1789 void convertToPEMFile(std::FILE& outputFile) const override;
77183aa7 1790
6d40ba53
FM
1791 [[nodiscard]] storvector_t convertToISCVector() const override;
1792 [[nodiscard]] std::string sign(const std::string& msg) const override;
f237f53e 1793 [[nodiscard]] bool verify(const std::string& message, const std::string& signature) const override;
6d40ba53 1794 [[nodiscard]] std::string getPublicKeyString() const override;
439974d7
PL
1795 void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
1796 void fromPublicKeyString(const std::string& content) override;
f7b9d3b7 1797 [[nodiscard]] bool checkKey(std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const override;
439974d7 1798
a2c6e554 1799 static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
439974d7 1800 {
a2c6e554 1801 return make_unique<OpenSSLEDDSADNSCryptoKeyEngine>(algorithm);
439974d7
PL
1802 }
1803
80e3989f
FM
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
439974d7 1808private:
9c1e4067
PL
1809 size_t d_len{0};
1810 int d_id{0};
439974d7 1811
80e3989f 1812 Key d_edkey;
439974d7 1813};
7581a35b 1814
80e3989f
FM
1815OpenSSLEDDSADNSCryptoKeyEngine::OpenSSLEDDSADNSCryptoKeyEngine(unsigned int algo) :
1816 DNSCryptoKeyEngine(algo),
1817 d_edkey(Key(nullptr, EVP_PKEY_free))
1818{
1819 int ret = RAND_status();
1820 if (ret != 1) {
1821 throw runtime_error(getName() + " insufficient entropy");
1822 }
1823
1824#ifdef HAVE_LIBCRYPTO_ED25519
1825 if (d_algorithm == 15) {
1826 d_len = 32;
1827 d_id = NID_ED25519;
1828 }
1829#endif
1830#ifdef HAVE_LIBCRYPTO_ED448
1831 if (d_algorithm == 16) {
1832 d_len = 57;
1833 d_id = NID_ED448;
1834 }
1835#endif
1836 if (d_len == 0) {
1837 throw runtime_error(getName() + " unknown algorithm " + std::to_string(d_algorithm));
1838 }
1839}
1840
1841int OpenSSLEDDSADNSCryptoKeyEngine::getBits() const
439974d7 1842{
80e3989f
FM
1843 return (int)d_len << 3;
1844}
1845
3bb98d97 1846bool OpenSSLEDDSADNSCryptoKeyEngine::checkKey([[maybe_unused]] std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages) const
80e3989f
FM
1847{
1848#if OPENSSL_VERSION_MAJOR >= 3
1849 auto ctx = KeyContext{EVP_PKEY_CTX_new_from_pkey(nullptr, d_edkey.get(), nullptr), EVP_PKEY_CTX_free};
1850 if (ctx == nullptr) {
1851 throw pdns::OpenSSL::error(getName(), "Failed to create context to check key");
1852 }
1853
1854 bool retval = true;
1855
1856 auto addOpenSSLErrorMessageOnFail = [errorMessages, &retval](const int errorCode, const auto defaultErrorMessage) {
1857 // Error code of -2 means the check is not supported for the algorithm, which is fine.
1858 if (errorCode != 1 && errorCode != -2) {
1859 retval = false;
1860
f7b9d3b7 1861 if (errorMessages.has_value()) {
80e3989f
FM
1862 const auto* errorMessage = ERR_reason_error_string(ERR_get_error());
1863 if (errorMessage == nullptr) {
f7b9d3b7 1864 errorMessages->get().push_back(defaultErrorMessage);
80e3989f
FM
1865 }
1866 else {
f7b9d3b7 1867 errorMessages->get().emplace_back(errorMessage);
80e3989f
FM
1868 }
1869 }
1870 }
1871 };
1872
1873 addOpenSSLErrorMessageOnFail(EVP_PKEY_param_check(ctx.get()), getName() + "Unknown OpenSSL error during key param check");
1874 addOpenSSLErrorMessageOnFail(EVP_PKEY_public_check(ctx.get()), getName() + "Unknown OpenSSL error during public key check");
1875 addOpenSSLErrorMessageOnFail(EVP_PKEY_private_check(ctx.get()), getName() + "Unknown OpenSSL error during private key check");
1876 addOpenSSLErrorMessageOnFail(EVP_PKEY_pairwise_check(ctx.get()), getName() + "Unknown OpenSSL error during key pairwise check");
1877
1878 return retval;
1879#else
1880 return (d_edkey ? true : false);
1881#endif
439974d7
PL
1882}
1883
6d40ba53 1884void OpenSSLEDDSADNSCryptoKeyEngine::create(unsigned int /* bits */)
439974d7 1885{
80e3989f 1886 auto pctx = KeyContext(EVP_PKEY_CTX_new_id(d_id, nullptr), EVP_PKEY_CTX_free);
5498c0a9 1887 if (!pctx) {
80e3989f 1888 throw pdns::OpenSSL::error(getName(), "Context initialization failed");
439974d7 1889 }
80e3989f 1890
5498c0a9 1891 if (EVP_PKEY_keygen_init(pctx.get()) < 1) {
80e3989f 1892 throw pdns::OpenSSL::error(getName(), "Keygen initialization failed");
aa2e4010 1893 }
80e3989f 1894
5498c0a9
RG
1895 EVP_PKEY* newKey = nullptr;
1896 if (EVP_PKEY_keygen(pctx.get(), &newKey) < 1) {
80e3989f 1897 throw pdns::OpenSSL::error(getName(), "Key generation failed");
439974d7 1898 }
80e3989f
FM
1899
1900 d_edkey.reset(newKey);
7581a35b 1901}
439974d7 1902
c97af739 1903void OpenSSLEDDSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename)
b14ffbb0
FM
1904{
1905 drc.d_algorithm = d_algorithm;
80e3989f 1906 d_edkey = Key(PEM_read_PrivateKey(&inputFile, nullptr, nullptr, nullptr), &EVP_PKEY_free);
b14ffbb0 1907 if (d_edkey == nullptr) {
c97af739
FM
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");
b14ffbb0
FM
1913 }
1914}
1915
a8b5e8ba 1916void OpenSSLEDDSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
77183aa7 1917{
6d40ba53 1918 auto ret = PEM_write_PrivateKey(&outputFile, d_edkey.get(), nullptr, nullptr, 0, nullptr, nullptr);
77183aa7 1919 if (ret == 0) {
80e3989f 1920 throw pdns::OpenSSL::error(getName(), "Could not convert private key to PEM");
77183aa7
FM
1921 }
1922}
1923
439974d7
PL
1924DNSCryptoKeyEngine::storvector_t OpenSSLEDDSADNSCryptoKeyEngine::convertToISCVector() const
1925{
1926 storvector_t storvect;
1927 string algorithm;
1928
9c1e4067 1929#ifdef HAVE_LIBCRYPTO_ED25519
594fc95b 1930 if (d_algorithm == 15) {
439974d7 1931 algorithm = "15 (ED25519)";
9c1e4067 1932 }
de4e1d0d 1933#endif
9c1e4067 1934#ifdef HAVE_LIBCRYPTO_ED448
594fc95b 1935 if (d_algorithm == 16) {
439974d7 1936 algorithm = "16 (ED448)";
9c1e4067
PL
1937 }
1938#endif
1939 if (algorithm.empty()) {
439974d7 1940 algorithm = " ? (?)";
9c1e4067 1941 }
439974d7 1942
e32a8d46 1943 storvect.emplace_back("Algorithm", algorithm);
439974d7
PL
1944
1945 string buf;
1946 size_t len = d_len;
1947 buf.resize(len);
6d40ba53
FM
1948
1949 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
5498c0a9 1950 if (EVP_PKEY_get_raw_private_key(d_edkey.get(), reinterpret_cast<unsigned char*>(&buf.at(0)), &len) < 1) {
80e3989f 1951 throw pdns::OpenSSL::error(getName(), "Could not get private key from d_edkey");
aa2e4010 1952 }
e32a8d46 1953 storvect.emplace_back("PrivateKey", buf);
439974d7
PL
1954 return storvect;
1955}
1956
1957std::string OpenSSLEDDSADNSCryptoKeyEngine::sign(const std::string& msg) const
1958{
80e3989f 1959 auto mdctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
5498c0a9 1960 if (!mdctx) {
80e3989f 1961 throw pdns::OpenSSL::error(getName(), "MD context initialization failed");
aa2e4010 1962 }
80e3989f
FM
1963 if (EVP_DigestSignInit(mdctx.get(), nullptr, nullptr, nullptr, d_edkey.get()) < 1) {
1964 throw pdns::OpenSSL::error(getName(), "Unable to initialize signer");
439974d7
PL
1965 }
1966
439974d7 1967 string msgToSign = msg;
439974d7 1968
b4d4e25c
PL
1969 size_t siglen = d_len * 2;
1970 string signature;
439974d7
PL
1971 signature.resize(siglen);
1972
5498c0a9 1973 if (EVP_DigestSign(mdctx.get(),
6d40ba53 1974 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
594fc95b 1975 reinterpret_cast<unsigned char*>(&signature.at(0)), &siglen,
6d40ba53 1976 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
594fc95b
FM
1977 reinterpret_cast<unsigned char*>(&msgToSign.at(0)), msgToSign.length())
1978 < 1) {
80e3989f 1979 throw pdns::OpenSSL::error(getName(), "Signing error");
439974d7 1980 }
5498c0a9 1981
439974d7
PL
1982 return signature;
1983}
1984
80e3989f 1985bool OpenSSLEDDSADNSCryptoKeyEngine::verify(const std::string& message, const std::string& signature) const
439974d7 1986{
80e3989f
FM
1987 auto ctx = MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1988 if (!ctx) {
1989 throw pdns::OpenSSL::error(getName(), "MD context initialization failed");
aa2e4010 1990 }
80e3989f
FM
1991 if (EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, d_edkey.get()) < 1) {
1992 throw pdns::OpenSSL::error(getName(), "Unable to initialize signer");
439974d7
PL
1993 }
1994
80e3989f 1995 auto ret = EVP_DigestVerify(ctx.get(),
6d40ba53 1996 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
80e3989f 1997 reinterpret_cast<const unsigned char*>(&signature.at(0)), signature.length(),
6d40ba53 1998 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
80e3989f 1999 reinterpret_cast<const unsigned char*>(&message.at(0)), message.length());
6d40ba53 2000 if (ret < 0) {
80e3989f 2001 throw pdns::OpenSSL::error(getName(), "Verification failure");
439974d7
PL
2002 }
2003
6d40ba53 2004 return (ret == 1);
439974d7
PL
2005}
2006
439974d7
PL
2007std::string OpenSSLEDDSADNSCryptoKeyEngine::getPublicKeyString() const
2008{
2009 string buf;
2010 size_t len = d_len;
2011 buf.resize(len);
6d40ba53
FM
2012
2013 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
5498c0a9 2014 if (EVP_PKEY_get_raw_public_key(d_edkey.get(), reinterpret_cast<unsigned char*>(&buf.at(0)), &len) < 1) {
80e3989f 2015 throw pdns::OpenSSL::error(getName(), "Unable to get public key from key struct");
439974d7 2016 }
594fc95b 2017
439974d7
PL
2018 return buf;
2019}
2020
80e3989f
FM
2021void OpenSSLEDDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
2022{
439974d7
PL
2023 drc.d_algorithm = atoi(stormap["algorithm"].c_str());
2024 if (drc.d_algorithm != d_algorithm) {
80e3989f 2025 throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc.d_algorithm) + " to a " + std::to_string(d_algorithm) + " key");
439974d7 2026 }
7581a35b 2027
6d40ba53 2028 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
80e3989f 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);
5498c0a9 2030 if (!d_edkey) {
80e3989f 2031 throw pdns::OpenSSL::error(getName(), "Could not create key structure from private key");
439974d7 2032 }
439974d7
PL
2033}
2034
2035void OpenSSLEDDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& content)
2036{
7270ecca
PL
2037 if (content.length() != d_len) {
2038 throw runtime_error(getName() + " wrong public key length for algorithm " + std::to_string(d_algorithm));
439974d7
PL
2039 }
2040
6d40ba53
FM
2041 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
2042 const auto* raw = reinterpret_cast<const unsigned char*>(content.c_str());
7270ecca 2043
80e3989f 2044 d_edkey = Key(EVP_PKEY_new_raw_public_key(d_id, nullptr, raw, d_len), EVP_PKEY_free);
5498c0a9 2045 if (!d_edkey) {
80e3989f 2046 throw pdns::OpenSSL::error(getName(), "Allocation of public key structure failed");
439974d7 2047 }
439974d7
PL
2048}
2049#endif // HAVE_LIBCRYPTO_EDDSA
7581a35b 2050
f4bf7b65
FM
2051namespace
2052{
2053const struct LoaderStruct
2054{
2055 LoaderStruct()
7581a35b 2056 {
f4bf7b65
FM
2057 DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
2058 DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA1NSEC3SHA1, &OpenSSLRSADNSCryptoKeyEngine::maker);
2059 DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA256, &OpenSSLRSADNSCryptoKeyEngine::maker);
2060 DNSCryptoKeyEngine::report(DNSSECKeeper::RSASHA512, &OpenSSLRSADNSCryptoKeyEngine::maker);
74d83458 2061#ifdef HAVE_LIBCRYPTO_ECDSA
f4bf7b65
FM
2062 DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA256, &OpenSSLECDSADNSCryptoKeyEngine::maker);
2063 DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA384, &OpenSSLECDSADNSCryptoKeyEngine::maker);
439974d7
PL
2064#endif
2065#ifdef HAVE_LIBCRYPTO_ED25519
f4bf7b65 2066 DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
439974d7
PL
2067#endif
2068#ifdef HAVE_LIBCRYPTO_ED448
f4bf7b65 2069 DNSCryptoKeyEngine::report(DNSSECKeeper::ED448, &OpenSSLEDDSADNSCryptoKeyEngine::maker);
de4e1d0d 2070#endif
f4bf7b65
FM
2071 }
2072} loaderOpenSSL;
7581a35b 2073}