2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
24 #include <openssl/crypto.h>
25 #include <openssl/ec.h>
30 #include <openssl/obj_mac.h>
31 #ifdef HAVE_LIBCRYPTO_ECDSA
32 #include <openssl/ecdsa.h>
34 #if defined(HAVE_LIBCRYPTO_ED25519) || defined(HAVE_LIBCRYPTO_ED448)
35 #include <openssl/evp.h>
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>
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"
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. */
58 static std::vector
<std::mutex
> openssllocks
;
62 static void openssl_pthreads_locking_callback(int mode
, int type
, const char* file
, int line
)
64 if (mode
& CRYPTO_LOCK
) {
65 openssllocks
.at(type
).lock();
68 openssllocks
.at(type
).unlock();
72 static unsigned long openssl_pthreads_id_callback(void)
74 return (unsigned long)pthread_self();
78 void openssl_thread_setup()
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
);
85 void openssl_thread_cleanup()
87 CRYPTO_set_locking_callback(nullptr);
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
)
101 static inline int RSA_set0_key(RSA
* rsakey
, BIGNUM
* n
, BIGNUM
* e
, BIGNUM
* d
)
104 BN_clear_free(rsakey
->n
);
108 BN_clear_free(rsakey
->e
);
112 BN_clear_free(rsakey
->d
);
118 static inline void RSA_get0_factors(const RSA
* rsakey
, const BIGNUM
** p
, const BIGNUM
** q
)
124 static inline int RSA_set0_factors(RSA
* rsakey
, BIGNUM
* p
, BIGNUM
* q
)
126 BN_clear_free(rsakey
->p
);
128 BN_clear_free(rsakey
->q
);
133 static inline void RSA_get0_crt_params(const RSA
* rsakey
, const BIGNUM
** dmp1
, const BIGNUM
** dmq1
, const BIGNUM
** iqmp
)
135 *dmp1
= rsakey
->dmp1
;
136 *dmq1
= rsakey
->dmq1
;
137 *iqmp
= rsakey
->iqmp
;
140 static inline int RSA_set0_crt_params(RSA
* rsakey
, BIGNUM
* dmp1
, BIGNUM
* dmq1
, BIGNUM
* iqmp
)
142 BN_clear_free(rsakey
->dmp1
);
144 BN_clear_free(rsakey
->dmq1
);
146 BN_clear_free(rsakey
->iqmp
);
151 #ifdef HAVE_LIBCRYPTO_ECDSA
152 static inline void ECDSA_SIG_get0(const ECDSA_SIG
* signature
, const BIGNUM
** pr
, const BIGNUM
** ps
)
158 static inline int ECDSA_SIG_set0(ECDSA_SIG
* signature
, BIGNUM
* pr
, BIGNUM
* ps
)
160 BN_clear_free(signature
->r
);
161 BN_clear_free(signature
->s
);
166 #endif /* HAVE_LIBCRYPTO_ECDSA */
168 #endif /* HAVE_RSA_GET0_KEY */
171 void openssl_thread_setup() {}
172 void openssl_thread_cleanup() {}
179 entropy
.reserve(1024);
182 for (int i
= 0; i
< 1024; i
+= 4) {
183 r
= dns_random_uint32();
184 entropy
.append((const char*)&r
, 4);
187 RAND_seed((const unsigned char*)entropy
.c_str(), 1024);
190 using BigNum
= unique_ptr
<BIGNUM
, decltype(&BN_clear_free
)>;
192 static auto mapToBN(const std::string
& componentName
, const std::map
<std::string
, std::string
>& stormap
, const std::string
& key
) -> BigNum
194 const std::string
& value
= stormap
.at(key
);
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
+ "`");
206 class OpenSSLRSADNSCryptoKeyEngine
: public DNSCryptoKeyEngine
209 explicit OpenSSLRSADNSCryptoKeyEngine(unsigned int algo
);
211 [[nodiscard
]] string
getName() const override
{ return "OpenSSL RSA"; }
212 [[nodiscard
]] int getBits() const override
;
213 void create(unsigned int bits
) override
;
216 * \brief Creates an RSA key engine from a PEM file.
218 * Receives an open file handle with PEM contents and creates an RSA key engine.
220 * \param[in] drc Key record contents to be populated.
222 * \param[in] inputFile An open file handle to a file containing RSA PEM contents.
224 * \param[in] filename Only used for providing filename information in error messages.
226 * \return An RSA key engine populated with the contents of the PEM file.
228 void createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, std::optional
<std::reference_wrapper
<const std::string
>> filename
= std::nullopt
) override
;
231 * \brief Writes this key's contents to a file.
233 * Receives an open file handle and writes this key's contents to the
236 * \param[in] outputFile An open file handle for writing.
238 * \exception std::runtime_error In case of OpenSSL errors.
240 void convertToPEMFile(std::FILE& outputFile
) const override
;
242 [[nodiscard
]] storvector_t
convertToISCVector() const override
;
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
;
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
;
254 static std::unique_ptr
<DNSCryptoKeyEngine
> maker(unsigned int algorithm
)
256 return make_unique
<OpenSSLRSADNSCryptoKeyEngine
>(algorithm
);
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;
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
;
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
);
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
)>;
287 using Key
= std::unique_ptr
<RSA
, decltype(&RSA_free
)>;
293 OpenSSLRSADNSCryptoKeyEngine::OpenSSLRSADNSCryptoKeyEngine(unsigned int algo
) :
294 DNSCryptoKeyEngine(algo
),
295 #if OPENSSL_VERSION_MAJOR >= 3
296 d_key(Key(nullptr, EVP_PKEY_free
))
298 d_key(Key(nullptr, RSA_free
))
301 int ret
= RAND_status();
303 throw runtime_error(getName() + " insufficient entropy");
307 int OpenSSLRSADNSCryptoKeyEngine::getBits() const
309 #if OPENSSL_VERSION_MAJOR >= 3
310 return EVP_PKEY_get_bits(d_key
.get());
312 return RSA_size(d_key
.get()) << 3;
316 void OpenSSLRSADNSCryptoKeyEngine::create(unsigned int bits
)
318 // When changing the bitsizes, also edit them in ::checkKey
319 if ((d_algorithm
== DNSSECKeeper::RSASHA1
|| d_algorithm
== DNSSECKeeper::RSASHA1NSEC3SHA1
) && (bits
< 512 || bits
> 4096)) {
321 throw runtime_error(getName() + " RSASHA1 key generation failed for invalid bits size " + std::to_string(bits
));
323 if (d_algorithm
== DNSSECKeeper::RSASHA256
&& (bits
< 512 || bits
> 4096)) {
325 throw runtime_error(getName() + " RSASHA256 key generation failed for invalid bits size " + std::to_string(bits
));
327 if (d_algorithm
== DNSSECKeeper::RSASHA512
&& (bits
< 1024 || bits
> 4096)) {
329 throw runtime_error(getName() + " RSASHA512 key generation failed for invalid bits size " + std::to_string(bits
));
332 auto exponent
= BigNum(BN_new(), BN_clear_free
);
334 throw runtime_error(getName() + " key generation failed, unable to allocate e");
337 /* RSA_F4 is a public exponent value of 65537 */
338 int res
= BN_set_word(exponent
.get(), RSA_F4
);
341 throw runtime_error(getName() + " key generation failed while setting e");
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");
350 if (EVP_PKEY_keygen_init(ctx
.get()) != 1) {
351 throw pdns::OpenSSL::error(getName(), "Could not initialize keygen");
354 if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx
.get(), (int)bits
) <= 0) {
355 throw pdns::OpenSSL::error(getName(), "Could not set keygen bits to " + std::to_string(bits
));
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");
362 EVP_PKEY
* key
= nullptr;
363 if (EVP_PKEY_generate(ctx
.get(), &key
) != 1) {
364 throw pdns::OpenSSL::error(getName(), "Could not generate key");
369 auto key
= Key(RSA_new(), RSA_free
);
371 throw runtime_error(getName() + " allocation of key structure failed");
374 res
= RSA_generate_key_ex(key
.get(), bits
, exponent
.get(), nullptr);
376 throw runtime_error(getName() + " key generation failed");
379 d_key
= std::move(key
);
383 void OpenSSLRSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, const std::optional
<std::reference_wrapper
<const std::string
>> filename
)
385 drc
.d_algorithm
= d_algorithm
;
387 #if OPENSSL_VERSION_MAJOR >= 3
388 EVP_PKEY
* key
= nullptr;
389 if (PEM_read_PrivateKey(&inputFile
, &key
, nullptr, nullptr) == nullptr) {
390 if (filename
.has_value()) {
391 throw pdns::OpenSSL::error(getName(), "Could not read private key from PEM file `" + filename
->get() + "`");
394 throw pdns::OpenSSL::error(getName(), "Could not read private key from PEM contents");
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() + "`");
405 throw runtime_error(getName() + ": Failed to read private key from PEM contents");
410 void OpenSSLRSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile
) const
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");
417 auto ret
= PEM_write_RSAPrivateKey(&outputFile
, d_key
.get(), nullptr, nullptr, 0, nullptr, nullptr);
419 throw runtime_error(getName() + ": Could not convert private key to PEM");
424 #if OPENSSL_VERSION_MAJOR >= 3
425 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamModulus() const
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");
431 return BigNum
{modulus
, BN_clear_free
};
434 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamPublicExponent() const
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");
440 return BigNum
{publicExponent
, BN_clear_free
};
443 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrivateExponent() const
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");
449 return BigNum
{privateExponent
, BN_clear_free
};
452 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrime1() const
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");
458 return BigNum
{prime1
, BN_clear_free
};
461 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamPrime2() const
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");
467 return BigNum
{prime2
, BN_clear_free
};
470 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamDmp1() const
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");
476 return BigNum
{dmp1
, BN_clear_free
};
479 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamDmq1() const
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");
485 return BigNum
{dmq1
, BN_clear_free
};
488 BigNum
OpenSSLRSADNSCryptoKeyEngine::getKeyParamIqmp() const
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");
494 return BigNum
{iqmp
, BN_clear_free
};
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
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
547 DNSCryptoKeyEngine::storvector_t
OpenSSLRSADNSCryptoKeyEngine::convertToISCVector() const
549 storvector_t storvect
;
550 using outputs_t
= vector
<pair
<string
, const BIGNUM
*>>;
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();
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();
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
);
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
);
592 outputs
.emplace_back("Exponent1", dmp1
);
593 outputs
.emplace_back("Exponent2", dmq1
);
594 outputs
.emplace_back("Coefficient", iqmp
);
596 string algorithm
= std::to_string(d_algorithm
);
597 switch (d_algorithm
) {
598 case DNSSECKeeper::RSASHA1
:
599 case DNSSECKeeper::RSASHA1NSEC3SHA1
:
600 algorithm
+= " (RSASHA1)";
602 case DNSSECKeeper::RSASHA256
:
603 algorithm
+= " (RSASHA256)";
605 case DNSSECKeeper::RSASHA512
:
606 algorithm
+= " (RSASHA512)";
611 storvect
.emplace_back("Algorithm", algorithm
);
613 for (const outputs_t::value_type
& value
: outputs
) {
615 tmp
.resize(BN_num_bytes(value
.second
));
616 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
617 int len
= BN_bn2bin(value
.second
, reinterpret_cast<unsigned char*>(tmp
.data()));
620 storvect
.emplace_back(value
.first
, tmp
);
627 std::size_t OpenSSLRSADNSCryptoKeyEngine::hashSize() const
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
;
638 throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm
));
642 const EVP_MD
* OpenSSLRSADNSCryptoKeyEngine::hasher() const
644 const EVP_MD
* messageDigest
= nullptr;
646 switch (d_algorithm
) {
647 case DNSSECKeeper::RSASHA1
:
648 case DNSSECKeeper::RSASHA1NSEC3SHA1
:
649 messageDigest
= EVP_sha1();
651 case DNSSECKeeper::RSASHA256
:
652 messageDigest
= EVP_sha256();
654 case DNSSECKeeper::RSASHA512
:
655 messageDigest
= EVP_sha512();
658 throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm
));
661 if (messageDigest
== nullptr) {
662 throw std::runtime_error("Could not retrieve a SHA implementation of size " + std::to_string(hashSize()) + " from OpenSSL");
665 return messageDigest
;
668 std::string
OpenSSLRSADNSCryptoKeyEngine::hash(const std::string
& message
) const
670 if (d_algorithm
== DNSSECKeeper::RSASHA1
|| d_algorithm
== DNSSECKeeper::RSASHA1NSEC3SHA1
) {
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()));
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()));
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()));
694 throw runtime_error(getName() + " does not support hash operation for algorithm " + std::to_string(d_algorithm
));
697 int OpenSSLRSADNSCryptoKeyEngine::hashSizeToKind(const size_t hashSize
)
700 case SHA_DIGEST_LENGTH
:
702 case SHA256_DIGEST_LENGTH
:
704 case SHA384_DIGEST_LENGTH
:
706 case SHA512_DIGEST_LENGTH
:
709 throw runtime_error("OpenSSL RSA does not handle hash of size " + std::to_string(hashSize
));
713 std::string
OpenSSLRSADNSCryptoKeyEngine::sign(const std::string
& message
) const
715 std::string signature
;
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");
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");
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");
734 signature
.resize(signatureLen
);
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");
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()));
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());
750 throw runtime_error(getName() + " failed to generate signature");
753 signature
.resize(signatureLen
);
759 bool OpenSSLRSADNSCryptoKeyEngine::verify(const std::string
& message
, const std::string
& signature
) const
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");
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");
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());
774 throw pdns::OpenSSL::error(getName(), "Failed to verify message signature");
779 string l_hash
= this->hash(message
);
780 int hashKind
= hashSizeToKind(l_hash
.size());
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());
788 std::string
OpenSSLRSADNSCryptoKeyEngine::getPublicKeyString() const
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();
795 // All the calls succeeded, we can take references to the BIGNUM pointers.
796 BIGNUM
* modulus
= modulusPtr
.get();
797 BIGNUM
* publicExponent
= publicExponentPtr
.get();
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
);
807 tmp
.resize(std::max(BN_num_bytes(publicExponent
), BN_num_bytes(modulus
)));
809 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
810 int len
= BN_bn2bin(publicExponent
, reinterpret_cast<unsigned char*>(&tmp
.at(0)));
812 keystring
.assign(1, (char)(unsigned int)len
);
815 keystring
.assign(1, 0);
816 uint16_t tempLen
= len
;
817 tempLen
= htons(tempLen
);
818 keystring
.append((char*)&tempLen
, 2);
820 keystring
.append(&tmp
.at(0), len
);
822 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
823 len
= BN_bn2bin(modulus
, reinterpret_cast<unsigned char*>(&tmp
.at(0)));
824 keystring
.append(&tmp
.at(0), len
);
829 void OpenSSLRSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent
& drc
, std::map
<std::string
, std::string
>& stormap
)
831 auto modulus
= mapToBN(getName(), stormap
, "modulus");
832 auto publicExponent
= mapToBN(getName(), stormap
, "publicexponent");
833 auto privateExponent
= mapToBN(getName(), stormap
, "privateexponent");
835 auto prime1
= mapToBN(getName(), stormap
, "prime1");
836 auto prime2
= mapToBN(getName(), stormap
, "prime2");
838 auto dmp1
= mapToBN(getName(), stormap
, "exponent1");
839 auto dmq1
= mapToBN(getName(), stormap
, "exponent2");
840 auto iqmp
= mapToBN(getName(), stormap
, "coefficient");
842 pdns::checked_stoi_into(drc
.d_algorithm
, stormap
["algorithm"]);
844 if (drc
.d_algorithm
!= d_algorithm
) {
845 throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc
.d_algorithm
) + " to a " + std::to_string(d_algorithm
) + " key");
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());
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");
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");
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");
867 auto key
= Key(RSA_new(), RSA_free
);
869 throw runtime_error(getName() + " allocation of key structure failed");
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());
877 d_key
= std::move(key
);
881 bool OpenSSLRSADNSCryptoKeyEngine::checkKey(std::optional
<std::reference_wrapper
<std::vector
<std::string
>>> errorMessages
) const
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)) {
887 if (errorMessages
.has_value()) {
888 errorMessages
->get().push_back("key is " + std::to_string(getBits()) + " bytes, should be between 512 and 4096");
891 if (d_algorithm
== DNSSECKeeper::RSASHA512
&& (getBits() < 1024 || getBits() > 4096)) {
893 if (errorMessages
.has_value()) {
894 errorMessages
->get().push_back("key is " + std::to_string(getBits()) + " bytes, should be between 1024 and 4096");
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");
904 if (EVP_PKEY_pairwise_check(ctx
.get()) != 1) {
906 if (RSA_check_key(d_key
.get()) != 1) {
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";
914 errorMessages
->get().emplace_back(errmsg
);
920 void OpenSSLRSADNSCryptoKeyEngine::fromPublicKeyString(const std::string
& content
)
922 string publicExponent
;
924 const size_t contentLen
= content
.length();
926 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
927 const auto* raw
= reinterpret_cast<const unsigned char*>(content
.c_str());
929 if (contentLen
< 1) {
930 throw runtime_error(getName() + " invalid input size for the public key");
934 const size_t exponentSize
= raw
[0];
935 if (contentLen
< (exponentSize
+ 2)) {
936 throw runtime_error(getName() + " invalid input size for the public key");
938 publicExponent
= content
.substr(1, exponentSize
);
939 modulus
= content
.substr(exponentSize
+ 1);
942 if (contentLen
< 3) {
943 throw runtime_error(getName() + " invalid input size for the public key");
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");
949 publicExponent
= content
.substr(3, exponentSize
);
950 modulus
= content
.substr(exponentSize
+ 3);
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
) {
956 throw runtime_error(getName() + " error loading public exponent (e) value of public key");
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
);
962 throw runtime_error(getName() + " error loading modulus (n) value of public key");
965 #if OPENSSL_VERSION_MAJOR >= 3
966 auto params
= makeKeyParams(modulusBN
.get(), publicExponentBN
.get(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
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");
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");
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");
984 auto key
= Key(RSA_new(), RSA_free
);
986 throw runtime_error(getName() + " allocation of key structure failed");
989 RSA_set0_key(key
.get(), modulusBN
.release(), publicExponentBN
.release(), nullptr);
990 d_key
= std::move(key
);
994 #ifdef HAVE_LIBCRYPTO_ECDSA
995 class OpenSSLECDSADNSCryptoKeyEngine
: public DNSCryptoKeyEngine
998 explicit OpenSSLECDSADNSCryptoKeyEngine(unsigned int algo
);
1000 [[nodiscard
]] string
getName() const override
{ return "OpenSSL ECDSA"; }
1001 [[nodiscard
]] int getBits() const override
;
1003 void create(unsigned int bits
) override
;
1006 * \brief Creates an ECDSA key engine from a PEM file.
1008 * Receives an open file handle with PEM contents and creates an ECDSA key engine.
1010 * \param[in] drc Key record contents to be populated.
1012 * \param[in] inputFile An open file handle to a file containing ECDSA PEM contents.
1014 * \param[in] filename Only used for providing filename information in error messages.
1016 * \return An ECDSA key engine populated with the contents of the PEM file.
1018 void createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, std::optional
<std::reference_wrapper
<const std::string
>> filename
= std::nullopt
) override
;
1021 * \brief Writes this key's contents to a file.
1023 * Receives an open file handle and writes this key's contents to the
1026 * \param[in] outputFile An open file handle for writing.
1028 * \exception std::runtime_error In case of OpenSSL errors.
1030 void convertToPEMFile(std::FILE& outputFile
) const override
;
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
;
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;
1046 static std::unique_ptr
<DNSCryptoKeyEngine
> maker(unsigned int algorithm
)
1048 return make_unique
<OpenSSLECDSADNSCryptoKeyEngine
>(algorithm
);
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
;
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
)>;
1064 using Key
= std::unique_ptr
<EC_KEY
, decltype(&EC_KEY_free
)>;
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
)>;
1073 std::string d_group_name
{};
1074 Group d_group
{nullptr, EC_GROUP_free
};
1076 #if OPENSSL_VERSION_MAJOR >= 3
1077 Key d_eckey
{Key(nullptr, EVP_PKEY_free
)};
1079 Key d_eckey
{Key(nullptr, EC_KEY_free
)};
1083 int OpenSSLECDSADNSCryptoKeyEngine::getBits() const
1088 OpenSSLECDSADNSCryptoKeyEngine::OpenSSLECDSADNSCryptoKeyEngine(unsigned int algo
) :
1089 DNSCryptoKeyEngine(algo
)
1090 #if OPENSSL_VERSION_MAJOR < 3
1092 d_eckey(Key(EC_KEY_new(), EC_KEY_free
))
1095 int ret
= RAND_status();
1097 throw runtime_error(getName() + " insufficient entropy");
1100 #if OPENSSL_VERSION_MAJOR < 3
1102 throw runtime_error(getName() + " allocation of key structure failed");
1108 if (d_algorithm
== 13) {
1109 d_group_name
= "P-256";
1111 d_id
= NID_X9_62_prime256v1
;
1113 else if (d_algorithm
== 14) {
1114 d_group_name
= "P-384";
1116 d_id
= NID_secp384r1
;
1119 throw runtime_error(getName() + " unknown algorithm " + std::to_string(d_algorithm
));
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");
1127 #if OPENSSL_VERSION_MAJOR < 3
1128 ret
= EC_KEY_set_group(d_eckey
.get(), d_group
.get());
1130 throw runtime_error(getName() + " setting key group failed");
1135 void OpenSSLECDSADNSCryptoKeyEngine::create(unsigned int bits
)
1137 if (bits
>> 3 != static_cast<unsigned int>(d_len
)) {
1138 throw runtime_error(getName() + " unknown key length of " + std::to_string(bits
) + " bits requested");
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");
1150 int res
= EC_KEY_generate_key(d_eckey
.get());
1152 throw runtime_error(getName() + " key generation failed");
1155 EC_KEY_set_asn1_flag(d_eckey
.get(), OPENSSL_EC_NAMED_CURVE
);
1159 void OpenSSLECDSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, std::optional
<std::reference_wrapper
<const std::string
>> filename
)
1161 drc
.d_algorithm
= d_algorithm
;
1163 #if OPENSSL_VERSION_MAJOR >= 3
1164 EVP_PKEY
* key
= nullptr;
1165 if (PEM_read_PrivateKey(&inputFile
, &key
, nullptr, nullptr) == nullptr) {
1166 if (filename
.has_value()) {
1167 throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM file `" + filename
->get() + "`");
1170 throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM contents");
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() + "`");
1181 throw runtime_error(getName() + ": Failed to read private key from PEM contents");
1184 int ret
= EC_KEY_set_group(d_eckey
.get(), d_group
.get());
1186 throw runtime_error(getName() + " setting key group failed");
1189 const BIGNUM
* privateKeyBN
= EC_KEY_get0_private_key(d_eckey
.get());
1191 auto pub_key
= Point(EC_POINT_new(d_group
.get()), EC_POINT_free
);
1193 throw runtime_error(getName() + " allocation of public key point failed");
1196 ret
= EC_POINT_mul(d_group
.get(), pub_key
.get(), privateKeyBN
, nullptr, nullptr, nullptr);
1198 throw runtime_error(getName() + " computing public key from private failed");
1201 ret
= EC_KEY_set_public_key(d_eckey
.get(), pub_key
.get());
1203 ERR_print_errors_fp(stderr
);
1204 throw runtime_error(getName() + " setting public key failed");
1207 EC_KEY_set_asn1_flag(d_eckey
.get(), OPENSSL_EC_NAMED_CURVE
);
1211 void OpenSSLECDSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile
) const
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");
1218 auto ret
= PEM_write_ECPrivateKey(&outputFile
, d_eckey
.get(), nullptr, nullptr, 0, nullptr, nullptr);
1220 throw runtime_error(getName() + ": Could not convert private key to PEM");
1225 #if OPENSSL_VERSION_MAJOR >= 3
1226 auto OpenSSLECDSADNSCryptoKeyEngine::getPrivateKey() const -> BigNum
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");
1232 return BigNum
{privateKey
, BN_clear_free
};
1236 DNSCryptoKeyEngine::storvector_t
OpenSSLECDSADNSCryptoKeyEngine::convertToISCVector() const
1238 storvector_t storvect
;
1241 if (d_algorithm
== 13) {
1242 algorithm
= "13 (ECDSAP256SHA256)";
1244 else if (d_algorithm
== 14) {
1245 algorithm
= "14 (ECDSAP384SHA384)";
1248 algorithm
= " ? (?)";
1251 storvect
.emplace_back("Algorithm", algorithm
);
1253 #if OPENSSL_VERSION_MAJOR >= 3
1254 auto privateKeyBN
= getPrivateKey();
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()));
1261 privateKey
.resize(len
);
1264 if (d_len
- len
!= 0) {
1265 prefix
.append(d_len
- len
, 0x00);
1268 storvect
.emplace_back("PrivateKey", prefix
+ privateKey
);
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");
1277 tmp
.resize(BN_num_bytes(key
));
1278 int len
= BN_bn2bin(key
, reinterpret_cast<unsigned char*>(&tmp
.at(0)));
1282 prefix
.append(d_len
- len
, 0x00);
1285 storvect
.emplace_back("PrivateKey", prefix
+ tmp
);
1291 std::string
OpenSSLECDSADNSCryptoKeyEngine::hash(const std::string
& message
) const
1293 if (getBits() == 256) {
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()));
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()));
1309 throw runtime_error(getName() + " does not support a hash size of " + std::to_string(getBits()) + " bits");
1312 const EVP_MD
* OpenSSLECDSADNSCryptoKeyEngine::hasher() const
1314 const EVP_MD
* messageDigest
= nullptr;
1316 switch (d_algorithm
) {
1317 case DNSSECKeeper::ECDSA256
:
1318 messageDigest
= EVP_sha256();
1320 case DNSSECKeeper::ECDSA384
:
1321 messageDigest
= EVP_sha384();
1324 throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm
));
1327 if (messageDigest
== nullptr) {
1328 throw std::runtime_error("Could not retrieve a SHA implementation of size " + std::to_string(hashSize()) + " from OpenSSL");
1331 return messageDigest
;
1334 std::size_t OpenSSLECDSADNSCryptoKeyEngine::hashSize() const
1336 switch (d_algorithm
) {
1337 case DNSSECKeeper::ECDSA256
:
1338 return SHA256_DIGEST_LENGTH
;
1339 case DNSSECKeeper::ECDSA384
:
1340 return SHA384_DIGEST_LENGTH
;
1342 throw runtime_error(getName() + " does not support hash operations for algorithm " + std::to_string(d_algorithm
));
1346 std::string
OpenSSLECDSADNSCryptoKeyEngine::sign(const std::string
& message
) const
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");
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");
1358 std::size_t signatureLen
= 0;
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");
1366 std::string signatureBuffer
;
1367 signatureBuffer
.resize(signatureLen
);
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");
1375 signatureBuffer
.resize(signatureLen
);
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");
1383 string l_hash
= this->hash(message
);
1385 auto signature
= Signature(ECDSA_do_sign((unsigned char*)l_hash
.c_str(), l_hash
.length(), d_eckey
.get()), ECDSA_SIG_free
);
1387 throw runtime_error(getName() + " failed to generate signature");
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) {
1401 ret
.append(d_len
- len
, 0x00);
1403 ret
.append(&tmp
.at(0), len
);
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) {
1408 ret
.append(d_len
- len
, 0x00);
1410 ret
.append(&tmp
.at(0), len
);
1415 bool OpenSSLECDSADNSCryptoKeyEngine::verify(const std::string
& message
, const std::string
& signature
) const
1417 if (signature
.length() != (static_cast<unsigned long>(d_len
) * 2)) {
1418 throw runtime_error(getName() + " invalid signature size " + std::to_string(signature
.length()));
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");
1429 auto sig
= Signature(ECDSA_SIG_new(), ECDSA_SIG_free
);
1431 throw runtime_error(getName() + " allocation of signature structure failed");
1433 ECDSA_SIG_set0(sig
.get(), rComponent
.release(), sComponent
.release());
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");
1441 // Because OPENSSL_free() is a macro.
1442 auto derBuffer
= unique_ptr
<unsigned char, void (*)(unsigned char*)>{derBufferPointer
, [](auto* buffer
) { OPENSSL_free(buffer
); }};
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");
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");
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());
1456 throw pdns::OpenSSL::error(getName(), "Could not verify message signature");
1461 string l_hash
= this->hash(message
);
1463 int ret
= ECDSA_do_verify((unsigned char*)l_hash
.c_str(), l_hash
.length(), sig
.get(), d_eckey
.get());
1465 throw runtime_error(getName() + " verify error");
1472 std::string
OpenSSLECDSADNSCryptoKeyEngine::getPublicKeyString() const
1474 #if OPENSSL_VERSION_MAJOR >= 3
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");
1480 std::string publicKey
{};
1481 publicKey
.resize(bufsize
);
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");
1489 publicKey
.resize(bufsize
);
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");
1496 auto ctx
= BigNumContext(BN_CTX_new(), BN_CTX_free
);
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");
1504 std::string publicKeyUncompressed
{};
1505 bufsize
= EC_POINT_point2oct(d_group
.get(), publicKeyECPoint
.get(), POINT_CONVERSION_UNCOMPRESSED
, nullptr, 0, nullptr);
1507 throw pdns::OpenSSL::error(getName(), "Failed to get public key binary buffer size");
1509 publicKeyUncompressed
.resize(bufsize
);
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);
1515 throw pdns::OpenSSL::error(getName(), "Failed to convert public key to oct");
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
1521 publicKeyUncompressed
.erase(0, 1);
1523 return publicKeyUncompressed
;
1525 std::string binaryPoint
;
1526 binaryPoint
.resize((d_len
* 2) + 1);
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);
1530 throw runtime_error(getName() + " exporting point to binary failed");
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);
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
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");
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");
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");
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");
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");
1572 void OpenSSLECDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent
& drc
, std::map
<std::string
, std::string
>& stormap
)
1574 drc
.d_algorithm
= atoi(stormap
["algorithm"].c_str());
1576 if (drc
.d_algorithm
!= d_algorithm
) {
1577 throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc
.d_algorithm
) + " to a " + std::to_string(d_algorithm
) + " key");
1580 auto privateKey
= mapToBN(getName(), stormap
, "privatekey");
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");
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");
1592 std::string publicKey
{};
1593 size_t bufsize
= EC_POINT_point2oct(d_group
.get(), publicKeyECPoint
.get(), POINT_CONVERSION_COMPRESSED
, nullptr, 0, nullptr);
1595 throw pdns::OpenSSL::error(getName(), "Failed to get public key binary buffer size");
1597 publicKey
.resize(bufsize
);
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);
1603 throw pdns::OpenSSL::error(getName(), "Failed to convert public key to oct");
1606 auto params
= makeKeyParams(d_group_name
, privateKey
.get(), std::make_optional(publicKey
));
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");
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");
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");
1624 int ret
= EC_KEY_set_private_key(d_eckey
.get(), privateKey
.get());
1626 throw runtime_error(getName() + " setting private key failed");
1629 auto pub_key
= Point(EC_POINT_new(d_group
.get()), EC_POINT_free
);
1631 throw runtime_error(getName() + " allocation of public key point failed");
1634 ret
= EC_POINT_mul(d_group
.get(), pub_key
.get(), privateKey
.get(), nullptr, nullptr, nullptr);
1636 throw runtime_error(getName() + " computing public key from private failed");
1639 ret
= EC_KEY_set_public_key(d_eckey
.get(), pub_key
.get());
1641 throw runtime_error(getName() + " setting public key failed");
1644 EC_KEY_set_asn1_flag(d_eckey
.get(), OPENSSL_EC_NAMED_CURVE
);
1648 bool OpenSSLECDSADNSCryptoKeyEngine::checkKey(std::optional
<std::reference_wrapper
<std::vector
<std::string
>>> errorMessages
) const
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");
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) {
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
);
1669 errorMessages
->get().emplace_back(errorMessage
);
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");
1683 if (EC_KEY_check_key(d_eckey
.get()) != 1) {
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";
1690 errorMessages
->get().push_back(errmsg
);
1697 void OpenSSLECDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string
& content
)
1699 #if OPENSSL_VERSION_MAJOR >= 3
1700 /* uncompressed point, from SEC1: "2.3.4 Octet-String-to-Elliptic-Curve-Point
1703 std::string publicKey
= "\x04";
1704 publicKey
.append(content
);
1706 auto params
= makeKeyParams(d_group_name
, nullptr, std::make_optional(publicKey
));
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");
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");
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");
1724 /* uncompressed point, from SEC1: "2.3.4 Octet-String-to-Elliptic-Curve-Point
1727 string ecdsaPoint
= "\x04";
1728 ecdsaPoint
.append(content
);
1730 auto pub_key
= Point(EC_POINT_new(d_group
.get()), EC_POINT_free
);
1732 throw runtime_error(getName() + " allocation of point structure failed");
1735 int ret
= EC_POINT_oct2point(d_group
.get(), pub_key
.get(), (unsigned char*)ecdsaPoint
.c_str(), ecdsaPoint
.length(), nullptr);
1737 throw runtime_error(getName() + " reading ECP point from binary failed");
1740 ret
= EC_KEY_set_private_key(d_eckey
.get(), nullptr);
1742 throw runtime_error(getName() + " setting private key failed");
1745 ret
= EC_KEY_set_public_key(d_eckey
.get(), pub_key
.get());
1747 throw runtime_error(getName() + " setting public key failed");
1753 #ifdef HAVE_LIBCRYPTO_EDDSA
1754 class OpenSSLEDDSADNSCryptoKeyEngine
: public DNSCryptoKeyEngine
1757 explicit OpenSSLEDDSADNSCryptoKeyEngine(unsigned int algo
);
1759 [[nodiscard
]] string
getName() const override
{ return "OpenSSL EdDSA"; }
1760 [[nodiscard
]] int getBits() const override
;
1762 void create(unsigned int bits
) override
;
1765 * \brief Creates an EDDSA key engine from a PEM file.
1767 * Receives an open file handle with PEM contents and creates an EDDSA key engine.
1769 * \param[in] drc Key record contents to be populated.
1771 * \param[in] inputFile An open file handle to a file containing EDDSA PEM contents.
1773 * \param[in] filename Only used for providing filename information in error messages.
1775 * \return An EDDSA key engine populated with the contents of the PEM file.
1777 void createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, std::optional
<std::reference_wrapper
<const std::string
>> filename
= std::nullopt
) override
;
1780 * \brief Writes this key's contents to a file.
1782 * Receives an open file handle and writes this key's contents to the
1785 * \param[in] outputFile An open file handle for writing.
1787 * \exception std::runtime_error In case of OpenSSL errors.
1789 void convertToPEMFile(std::FILE& outputFile
) const override
;
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
;
1799 static std::unique_ptr
<DNSCryptoKeyEngine
> maker(unsigned int algorithm
)
1801 return make_unique
<OpenSSLEDDSADNSCryptoKeyEngine
>(algorithm
);
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
)>;
1815 OpenSSLEDDSADNSCryptoKeyEngine::OpenSSLEDDSADNSCryptoKeyEngine(unsigned int algo
) :
1816 DNSCryptoKeyEngine(algo
),
1817 d_edkey(Key(nullptr, EVP_PKEY_free
))
1819 int ret
= RAND_status();
1821 throw runtime_error(getName() + " insufficient entropy");
1824 #ifdef HAVE_LIBCRYPTO_ED25519
1825 if (d_algorithm
== 15) {
1830 #ifdef HAVE_LIBCRYPTO_ED448
1831 if (d_algorithm
== 16) {
1837 throw runtime_error(getName() + " unknown algorithm " + std::to_string(d_algorithm
));
1841 int OpenSSLEDDSADNSCryptoKeyEngine::getBits() const
1843 return (int)d_len
<< 3;
1846 bool OpenSSLEDDSADNSCryptoKeyEngine::checkKey([[maybe_unused
]] std::optional
<std::reference_wrapper
<std::vector
<std::string
>>> errorMessages
) const
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");
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) {
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
);
1867 errorMessages
->get().emplace_back(errorMessage
);
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");
1880 return (d_edkey
? true : false);
1884 void OpenSSLEDDSADNSCryptoKeyEngine::create(unsigned int /* bits */)
1886 auto pctx
= KeyContext(EVP_PKEY_CTX_new_id(d_id
, nullptr), EVP_PKEY_CTX_free
);
1888 throw pdns::OpenSSL::error(getName(), "Context initialization failed");
1891 if (EVP_PKEY_keygen_init(pctx
.get()) < 1) {
1892 throw pdns::OpenSSL::error(getName(), "Keygen initialization failed");
1895 EVP_PKEY
* newKey
= nullptr;
1896 if (EVP_PKEY_keygen(pctx
.get(), &newKey
) < 1) {
1897 throw pdns::OpenSSL::error(getName(), "Key generation failed");
1900 d_edkey
.reset(newKey
);
1903 void OpenSSLEDDSADNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, std::optional
<std::reference_wrapper
<const std::string
>> filename
)
1905 drc
.d_algorithm
= d_algorithm
;
1906 d_edkey
= Key(PEM_read_PrivateKey(&inputFile
, nullptr, nullptr, nullptr), &EVP_PKEY_free
);
1907 if (d_edkey
== nullptr) {
1908 if (filename
.has_value()) {
1909 throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM file `" + filename
->get() + "`");
1912 throw pdns::OpenSSL::error(getName(), "Failed to read private key from PEM contents");
1916 void OpenSSLEDDSADNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile
) const
1918 auto ret
= PEM_write_PrivateKey(&outputFile
, d_edkey
.get(), nullptr, nullptr, 0, nullptr, nullptr);
1920 throw pdns::OpenSSL::error(getName(), "Could not convert private key to PEM");
1924 DNSCryptoKeyEngine::storvector_t
OpenSSLEDDSADNSCryptoKeyEngine::convertToISCVector() const
1926 storvector_t storvect
;
1929 #ifdef HAVE_LIBCRYPTO_ED25519
1930 if (d_algorithm
== 15) {
1931 algorithm
= "15 (ED25519)";
1934 #ifdef HAVE_LIBCRYPTO_ED448
1935 if (d_algorithm
== 16) {
1936 algorithm
= "16 (ED448)";
1939 if (algorithm
.empty()) {
1940 algorithm
= " ? (?)";
1943 storvect
.emplace_back("Algorithm", algorithm
);
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) {
1951 throw pdns::OpenSSL::error(getName(), "Could not get private key from d_edkey");
1953 storvect
.emplace_back("PrivateKey", buf
);
1957 std::string
OpenSSLEDDSADNSCryptoKeyEngine::sign(const std::string
& msg
) const
1959 auto mdctx
= MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free
);
1961 throw pdns::OpenSSL::error(getName(), "MD context initialization failed");
1963 if (EVP_DigestSignInit(mdctx
.get(), nullptr, nullptr, nullptr, d_edkey
.get()) < 1) {
1964 throw pdns::OpenSSL::error(getName(), "Unable to initialize signer");
1967 string msgToSign
= msg
;
1969 size_t siglen
= d_len
* 2;
1971 signature
.resize(siglen
);
1973 if (EVP_DigestSign(mdctx
.get(),
1974 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1975 reinterpret_cast<unsigned char*>(&signature
.at(0)), &siglen
,
1976 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1977 reinterpret_cast<unsigned char*>(&msgToSign
.at(0)), msgToSign
.length())
1979 throw pdns::OpenSSL::error(getName(), "Signing error");
1985 bool OpenSSLEDDSADNSCryptoKeyEngine::verify(const std::string
& message
, const std::string
& signature
) const
1987 auto ctx
= MessageDigestContext(EVP_MD_CTX_new(), EVP_MD_CTX_free
);
1989 throw pdns::OpenSSL::error(getName(), "MD context initialization failed");
1991 if (EVP_DigestVerifyInit(ctx
.get(), nullptr, nullptr, nullptr, d_edkey
.get()) < 1) {
1992 throw pdns::OpenSSL::error(getName(), "Unable to initialize signer");
1995 auto ret
= EVP_DigestVerify(ctx
.get(),
1996 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1997 reinterpret_cast<const unsigned char*>(&signature
.at(0)), signature
.length(),
1998 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
1999 reinterpret_cast<const unsigned char*>(&message
.at(0)), message
.length());
2001 throw pdns::OpenSSL::error(getName(), "Verification failure");
2007 std::string
OpenSSLEDDSADNSCryptoKeyEngine::getPublicKeyString() const
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) {
2015 throw pdns::OpenSSL::error(getName(), "Unable to get public key from key struct");
2021 void OpenSSLEDDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent
& drc
, std::map
<std::string
, std::string
>& stormap
)
2023 drc
.d_algorithm
= atoi(stormap
["algorithm"].c_str());
2024 if (drc
.d_algorithm
!= d_algorithm
) {
2025 throw runtime_error(getName() + " tried to feed an algorithm " + std::to_string(drc
.d_algorithm
) + " to a " + std::to_string(d_algorithm
) + " key");
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
);
2031 throw pdns::OpenSSL::error(getName(), "Could not create key structure from private key");
2035 void OpenSSLEDDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string
& content
)
2037 if (content
.length() != d_len
) {
2038 throw runtime_error(getName() + " wrong public key length for algorithm " + std::to_string(d_algorithm
));
2041 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
2042 const auto* raw
= reinterpret_cast<const unsigned char*>(content
.c_str());
2044 d_edkey
= Key(EVP_PKEY_new_raw_public_key(d_id
, nullptr, raw
, d_len
), EVP_PKEY_free
);
2046 throw pdns::OpenSSL::error(getName(), "Allocation of public key structure failed");
2049 #endif // HAVE_LIBCRYPTO_EDDSA
2053 const struct LoaderStruct
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
);
2061 #ifdef HAVE_LIBCRYPTO_ECDSA
2062 DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA256
, &OpenSSLECDSADNSCryptoKeyEngine::maker
);
2063 DNSCryptoKeyEngine::report(DNSSECKeeper::ECDSA384
, &OpenSSLECDSADNSCryptoKeyEngine::maker
);
2065 #ifdef HAVE_LIBCRYPTO_ED25519
2066 DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519
, &OpenSSLEDDSADNSCryptoKeyEngine::maker
);
2068 #ifdef HAVE_LIBCRYPTO_ED448
2069 DNSCryptoKeyEngine::report(DNSSECKeeper::ED448
, &OpenSSLEDDSADNSCryptoKeyEngine::maker
);