1 #include <openssl/evp.h>
2 #include <openssl/pem.h>
10 #include "dnssecinfra.hh"
11 #include "dnsseckeeper.hh"
13 class SodiumED25519DNSCryptoKeyEngine
: public DNSCryptoKeyEngine
16 explicit SodiumED25519DNSCryptoKeyEngine(unsigned int algo
) :
17 DNSCryptoKeyEngine(algo
)
19 string
getName() const override
{ return "Sodium ED25519"; }
20 void create(unsigned int bits
) override
;
22 #if defined(HAVE_LIBCRYPTO_ED25519)
24 * \brief Creates an ED25519 key engine from a PEM file.
26 * Receives an open file handle with PEM contents and creates an ED25519 key engine.
28 * \param[in] drc Key record contents to be populated.
30 * \param[in] inputFile An open file handle to a file containing ED25519 PEM contents.
32 * \param[in] filename Only used for providing filename information in error messages.
34 * \return An ED25519 key engine populated with the contents of the PEM file.
36 void createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, std::optional
<std::reference_wrapper
<const std::string
>> filename
= std::nullopt
) override
;
39 * \brief Writes this key's contents to a file.
41 * Receives an open file handle and writes this key's contents to the
44 * \param[in] outputFile An open file handle for writing.
46 * \exception std::runtime_error In case of OpenSSL errors.
48 void convertToPEMFile(std::FILE& outputFile
) const override
;
51 [[nodiscard
]] storvector_t
convertToISCVector() const override
;
52 [[nodiscard
]] std::string
sign(const std::string
& msg
) const override
;
53 [[nodiscard
]] bool verify(const std::string
& msg
, const std::string
& signature
) const override
;
54 [[nodiscard
]] std::string
getPublicKeyString() const override
;
55 [[nodiscard
]] int getBits() const override
;
56 void fromISCMap(DNSKEYRecordContent
& drc
, std::map
<std::string
, std::string
>& stormap
) override
;
57 void fromPublicKeyString(const std::string
& content
) override
;
59 static std::unique_ptr
<DNSCryptoKeyEngine
> maker(unsigned int algorithm
)
61 return make_unique
<SodiumED25519DNSCryptoKeyEngine
>(algorithm
);
65 unsigned char d_pubkey
[crypto_sign_ed25519_PUBLICKEYBYTES
];
66 unsigned char d_seckey
[crypto_sign_ed25519_SECRETKEYBYTES
];
69 void SodiumED25519DNSCryptoKeyEngine::create(unsigned int bits
)
71 if (bits
!= crypto_sign_ed25519_SEEDBYTES
* 8) {
72 throw runtime_error("Unsupported key length of " + std::to_string(bits
) + " bits requested, SodiumED25519 class");
74 crypto_sign_ed25519_keypair(d_pubkey
, d_seckey
);
77 #if defined(HAVE_LIBCRYPTO_ED25519)
78 void SodiumED25519DNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent
& drc
, std::FILE& inputFile
, std::optional
<std::reference_wrapper
<const std::string
>> filename
)
80 drc
.d_algorithm
= d_algorithm
;
81 auto key
= std::unique_ptr
<EVP_PKEY
, decltype(&EVP_PKEY_free
)>(PEM_read_PrivateKey(&inputFile
, nullptr, nullptr, nullptr), &EVP_PKEY_free
);
83 if (filename
.has_value()) {
84 throw runtime_error(getName() + ": Failed to read private key from PEM file `" + filename
->get() + "`");
87 throw runtime_error(getName() + ": Failed to read private key from PEM contents");
90 // The secret key is 64 bytes according to libsodium. But OpenSSL returns 32 in
91 // secKeyLen. Perhaps secret key means private key + public key in libsodium terms.
92 std::size_t secKeyLen
= crypto_sign_ed25519_SECRETKEYBYTES
;
93 int ret
= EVP_PKEY_get_raw_private_key(key
.get(), d_seckey
, &secKeyLen
);
95 if (filename
.has_value()) {
96 throw runtime_error(getName() + ": Failed to get private key from PEM file contents `" + filename
->get() + "`");
99 throw runtime_error(getName() + ": Failed to get private key from PEM contents");
102 std::size_t pubKeyLen
= crypto_sign_ed25519_PUBLICKEYBYTES
;
103 ret
= EVP_PKEY_get_raw_public_key(key
.get(), d_pubkey
, &pubKeyLen
);
105 if (filename
.has_value()) {
106 throw runtime_error(getName() + ": Failed to get public key from PEM file contents `" + filename
->get() + "`");
109 throw runtime_error(getName() + ": Failed to get public key from PEM contents");
112 // It looks like libsodium expects the public key to be appended to the private key,
113 // creating the "secret key" mentioned above.
114 memcpy(d_seckey
+ secKeyLen
, d_pubkey
, pubKeyLen
);
117 void SodiumED25519DNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile
) const
119 auto key
= std::unique_ptr
<EVP_PKEY
, void (*)(EVP_PKEY
*)>(EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519
, nullptr, d_seckey
, crypto_sign_ed25519_SEEDBYTES
), EVP_PKEY_free
);
120 if (key
== nullptr) {
121 throw runtime_error(getName() + ": Could not create private key from buffer");
124 auto ret
= PEM_write_PrivateKey(&outputFile
, key
.get(), nullptr, nullptr, 0, nullptr, nullptr);
126 throw runtime_error(getName() + ": Could not convert private key to PEM");
131 int SodiumED25519DNSCryptoKeyEngine::getBits() const
133 return crypto_sign_ed25519_SEEDBYTES
* 8;
136 DNSCryptoKeyEngine::storvector_t
SodiumED25519DNSCryptoKeyEngine::convertToISCVector() const
139 Private-key-format: v1.2
140 Algorithm: 15 (ED25519)
141 PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=
144 storvector_t storvector
;
145 string algorithm
= "15 (ED25519)";
147 storvector
.emplace_back("Algorithm", algorithm
);
149 storvector
.emplace_back("PrivateKey", string((char*)d_seckey
, crypto_sign_ed25519_SEEDBYTES
));
153 void SodiumED25519DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent
& drc
, std::map
<std::string
, std::string
>& stormap
)
156 Private-key-format: v1.2
157 Algorithm: 15 (ED25519)
158 PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=
161 pdns::checked_stoi_into(drc
.d_algorithm
, stormap
["algorithm"]);
162 string privateKey
= stormap
["privatekey"];
164 if (privateKey
.length() != crypto_sign_ed25519_SEEDBYTES
)
165 throw runtime_error("Seed size mismatch in ISCMap, SodiumED25519 class");
167 auto seed
= std::make_unique
<unsigned char[]>(crypto_sign_ed25519_SEEDBYTES
);
169 memcpy(seed
.get(), privateKey
.c_str(), crypto_sign_ed25519_SEEDBYTES
);
170 crypto_sign_ed25519_seed_keypair(d_pubkey
, d_seckey
, seed
.get());
173 std::string
SodiumED25519DNSCryptoKeyEngine::getPublicKeyString() const
175 return string((char*)d_pubkey
, crypto_sign_ed25519_PUBLICKEYBYTES
);
178 void SodiumED25519DNSCryptoKeyEngine::fromPublicKeyString(const std::string
& input
)
180 if (input
.length() != crypto_sign_ed25519_PUBLICKEYBYTES
)
181 throw runtime_error("Public key size mismatch, SodiumED25519 class");
183 memcpy(d_pubkey
, input
.c_str(), crypto_sign_ed25519_PUBLICKEYBYTES
);
186 std::string
SodiumED25519DNSCryptoKeyEngine::sign(const std::string
& msg
) const
188 unsigned char signature
[crypto_sign_ed25519_BYTES
];
190 // https://doc.libsodium.org/public-key_cryptography/public-key_signatures#detached-mode:
191 // It is safe to ignore siglen and always consider a signature as crypto_sign_BYTES
192 // bytes long; shorter signatures will be transparently padded with zeros if necessary.
193 crypto_sign_ed25519_detached(signature
, nullptr, (const unsigned char*)msg
.c_str(), msg
.length(), d_seckey
);
195 return {(const char*)signature
, crypto_sign_ed25519_BYTES
};
198 bool SodiumED25519DNSCryptoKeyEngine::verify(const std::string
& msg
, const std::string
& signature
) const
200 if (signature
.length() != crypto_sign_ed25519_BYTES
) {
204 return crypto_sign_ed25519_verify_detached((const unsigned char*)signature
.c_str(), (const unsigned char*)msg
.c_str(), msg
.length(), d_pubkey
) == 0;
209 const struct LoaderSodiumStruct
213 DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519
, &SodiumED25519DNSCryptoKeyEngine::maker
);