]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/decafsigners.cc
Meson: Separate test files from common files
[thirdparty/pdns.git] / pdns / decafsigners.cc
1 #include <openssl/err.h>
2 #include <openssl/pem.h>
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 #pragma GCC diagnostic push
7 #pragma GCC diagnostic ignored "-Wdeprecated-copy"
8 #include <decaf.hxx>
9 #pragma GCC diagnostic pop
10 #include <decaf/eddsa.hxx>
11 #pragma GCC diagnostic push
12 #pragma GCC diagnostic ignored "-Wshadow"
13 #include <decaf/spongerng.hxx>
14 #pragma GCC diagnostic pop
15 #include "dnsseckeeper.hh"
16
17 #include "dnssecinfra.hh"
18
19 using namespace decaf;
20
21 class DecafED25519DNSCryptoKeyEngine : public DNSCryptoKeyEngine
22 {
23 public:
24 explicit DecafED25519DNSCryptoKeyEngine(unsigned int algo) :
25 DNSCryptoKeyEngine(algo)
26 {
27 }
28 string getName() const override { return "Decaf ED25519"; }
29 void create(unsigned int bits) override;
30
31 #if defined(HAVE_LIBCRYPTO_ED25519)
32 /**
33 * \brief Creates an ED25519 key engine from a PEM file.
34 *
35 * Receives an open file handle with PEM contents and creates an ED25519 key engine.
36 *
37 * \param[in] drc Key record contents to be populated.
38 *
39 * \param[in] inputFile An open file handle to a file containing ED25519 PEM contents.
40 *
41 * \param[in] filename Only used for providing filename information in error messages.
42 *
43 * \return An ED25519 key engine populated with the contents of the PEM file.
44 */
45 void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
46
47 /**
48 * \brief Writes this key's contents to a file.
49 *
50 * Receives an open file handle and writes this key's contents to the
51 * file.
52 *
53 * \param[in] outputFile An open file handle for writing.
54 *
55 * \exception std::runtime_error In case of OpenSSL errors.
56 */
57 void convertToPEMFile(std::FILE& outputFile) const override;
58 #endif
59
60 [[nodiscard]] storvector_t convertToISCVector() const override;
61 [[nodiscard]] std::string sign(const std::string& msg) const override;
62 [[nodiscard]] bool verify(const std::string& msg, const std::string& signature) const override;
63 [[nodiscard]] std::string getPublicKeyString() const override;
64 [[nodiscard]] int getBits() const override;
65 void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
66 void fromPublicKeyString(const std::string& content) override;
67
68 static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
69 {
70 return make_unique<DecafED25519DNSCryptoKeyEngine>(algorithm);
71 }
72
73 private:
74 unsigned char d_pubkey[DECAF_EDDSA_25519_PUBLIC_BYTES];
75 unsigned char d_seckey[DECAF_EDDSA_25519_PRIVATE_BYTES];
76 };
77
78 void DecafED25519DNSCryptoKeyEngine::create(unsigned int bits)
79 {
80 if (bits != (unsigned int)getBits()) {
81 throw runtime_error("Unsupported key length of " + std::to_string(bits) + " bits requested, DecafED25519 class");
82 }
83
84 SpongeRng rng("/dev/urandom");
85
86 typename EdDSA<IsoEd25519>::PrivateKey priv(rng);
87 typename EdDSA<IsoEd25519>::PublicKey pub(priv);
88
89 priv.serialize_into(d_seckey);
90 pub.serialize_into(d_pubkey);
91 }
92
93 #if defined(HAVE_LIBCRYPTO_ED25519)
94 void DecafED25519DNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename)
95 {
96 drc.d_algorithm = d_algorithm;
97 auto key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>(PEM_read_PrivateKey(&inputFile, nullptr, nullptr, nullptr), &EVP_PKEY_free);
98 if (key == nullptr) {
99 if (filename.has_value()) {
100 throw runtime_error(getName() + ": Failed to read private key from PEM file `" + filename->get() + "`");
101 }
102
103 throw runtime_error(getName() + ": Failed to read private key from PEM contents");
104 }
105
106 std::size_t keylen = DECAF_EDDSA_25519_PRIVATE_BYTES;
107 int ret = EVP_PKEY_get_raw_private_key(key.get(), d_seckey, &keylen);
108 if (ret == 0) {
109 if (filename.has_value()) {
110 throw runtime_error(getName() + ": Failed to get private key from PEM file contents `" + filename->get() + "`");
111 }
112
113 throw runtime_error(getName() + ": Failed to get private key from PEM contents");
114 }
115
116 keylen = DECAF_EDDSA_25519_PUBLIC_BYTES;
117 ret = EVP_PKEY_get_raw_public_key(key.get(), d_pubkey, &keylen);
118 if (ret == 0) {
119 if (filename.has_value()) {
120 throw runtime_error(getName() + ": Failed to get public key from PEM file contents `" + filename->get() + "`");
121 }
122
123 throw runtime_error(getName() + ": Failed to get public key from PEM contents");
124 }
125 }
126
127 void DecafED25519DNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
128 {
129 auto key = std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)>(EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, d_seckey, DECAF_EDDSA_25519_PRIVATE_BYTES), EVP_PKEY_free);
130 if (key == nullptr) {
131 throw runtime_error(getName() + ": Could not create private key from buffer");
132 }
133
134 auto ret = PEM_write_PrivateKey(&outputFile, key.get(), nullptr, nullptr, 0, nullptr, nullptr);
135 if (ret == 0) {
136 throw runtime_error(getName() + ": Could not convert private key to PEM");
137 }
138 }
139 #endif
140
141 int DecafED25519DNSCryptoKeyEngine::getBits() const
142 {
143 return DECAF_EDDSA_25519_PRIVATE_BYTES << 3;
144 }
145
146 DNSCryptoKeyEngine::storvector_t DecafED25519DNSCryptoKeyEngine::convertToISCVector() const
147 {
148 /*
149 Private-key-format: v1.2
150 Algorithm: 15 (ED25519)
151 PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=
152 */
153
154 auto storvector = storvector_t{
155 {"Algorithm", "15 (ED25519)"},
156 {"PrivateKey", string((char*)d_seckey, DECAF_EDDSA_25519_PRIVATE_BYTES)},
157 };
158
159 return storvector;
160 }
161
162 void DecafED25519DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
163 {
164 /*
165 Private-key-format: v1.2
166 Algorithm: 15 (ED25519)
167 PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=
168 */
169
170 pdns::checked_stoi_into(drc.d_algorithm, stormap["algorithm"]);
171 string privateKey = stormap["privatekey"];
172
173 if (privateKey.length() != DECAF_EDDSA_25519_PRIVATE_BYTES)
174 throw runtime_error("Private key size mismatch in ISCMap, DecafED25519 class");
175
176 typename EdDSA<IsoEd25519>::PrivateKey priv(Block((const unsigned char*)privateKey.c_str(), DECAF_EDDSA_25519_PRIVATE_BYTES));
177 typename EdDSA<IsoEd25519>::PublicKey pub(priv);
178
179 priv.serialize_into(d_seckey);
180 pub.serialize_into(d_pubkey);
181 }
182
183 std::string DecafED25519DNSCryptoKeyEngine::getPublicKeyString() const
184 {
185 return string((char*)d_pubkey, DECAF_EDDSA_25519_PUBLIC_BYTES);
186 }
187
188 void DecafED25519DNSCryptoKeyEngine::fromPublicKeyString(const std::string& input)
189 {
190 if (input.length() != DECAF_EDDSA_25519_PUBLIC_BYTES)
191 throw runtime_error("Public key size mismatch, DecafED25519 class");
192
193 memcpy(d_pubkey, input.c_str(), DECAF_EDDSA_25519_PUBLIC_BYTES);
194 }
195
196 std::string DecafED25519DNSCryptoKeyEngine::sign(const std::string& msg) const
197 {
198 typename EdDSA<IsoEd25519>::PrivateKey priv(Block(d_seckey, DECAF_EDDSA_25519_PRIVATE_BYTES));
199
200 SecureBuffer message(msg.begin(), msg.end());
201
202 SecureBuffer sig = priv.sign(message);
203
204 return string(sig.begin(), sig.end());
205 }
206
207 bool DecafED25519DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const
208 {
209 if (signature.length() != DECAF_EDDSA_25519_SIGNATURE_BYTES)
210 return false;
211
212 typename EdDSA<IsoEd25519>::PublicKey pub(Block(d_pubkey, DECAF_EDDSA_25519_PUBLIC_BYTES));
213
214 SecureBuffer sig(signature.begin(), signature.end());
215 SecureBuffer message(msg.begin(), msg.end());
216
217 try {
218 pub.verify(sig, message);
219 }
220 catch (const CryptoException& e) {
221 return false;
222 }
223
224 return true;
225 }
226
227 class DecafED448DNSCryptoKeyEngine : public DNSCryptoKeyEngine
228 {
229 public:
230 explicit DecafED448DNSCryptoKeyEngine(unsigned int algo) :
231 DNSCryptoKeyEngine(algo)
232 {
233 }
234 string getName() const override { return "Decaf ED448"; }
235 void create(unsigned int bits) override;
236
237 #if defined(HAVE_LIBCRYPTO_ED448)
238 /**
239 * \brief Creates an ED448 key engine from a PEM file.
240 *
241 * Receives an open file handle with PEM contents and creates an ED448 key engine.
242 *
243 * \param[in] drc Key record contents to be populated.
244 *
245 * \param[in] inputFile An open file handle to a file containing ED448 PEM contents.
246 *
247 * \param[in] filename Only used for providing filename information in error messages.
248 *
249 * \return An ED448 key engine populated with the contents of the PEM file.
250 */
251 void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
252
253 /**
254 * \brief Writes this key's contents to a file.
255 *
256 * Receives an open file handle and writes this key's contents to the
257 * file.
258 *
259 * \param[in] outputFile An open file handle for writing.
260 *
261 * \exception std::runtime_error In case of OpenSSL errors.
262 */
263 void convertToPEMFile(std::FILE& outputFile) const override;
264 #endif
265
266 storvector_t convertToISCVector() const override;
267 std::string sign(const std::string& msg) const override;
268 bool verify(const std::string& msg, const std::string& signature) const override;
269 std::string getPublicKeyString() const override;
270 int getBits() const override;
271 void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap) override;
272 void fromPublicKeyString(const std::string& content) override;
273
274 static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
275 {
276 return make_unique<DecafED448DNSCryptoKeyEngine>(algorithm);
277 }
278
279 private:
280 unsigned char d_pubkey[DECAF_EDDSA_448_PUBLIC_BYTES];
281 unsigned char d_seckey[DECAF_EDDSA_448_PRIVATE_BYTES];
282 };
283
284 void DecafED448DNSCryptoKeyEngine::create(unsigned int bits)
285 {
286 if (bits != (unsigned int)getBits()) {
287 throw runtime_error("Unsupported key length of " + std::to_string(bits) + " bits requested, DecafED448 class");
288 }
289
290 SpongeRng rng("/dev/urandom");
291
292 typename EdDSA<Ed448Goldilocks>::PrivateKey priv(rng);
293 typename EdDSA<Ed448Goldilocks>::PublicKey pub(priv);
294
295 priv.serialize_into(d_seckey);
296 pub.serialize_into(d_pubkey);
297 }
298
299 #if defined(HAVE_LIBCRYPTO_ED448)
300 void DecafED448DNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename)
301 {
302 drc.d_algorithm = d_algorithm;
303 auto key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>(PEM_read_PrivateKey(&inputFile, nullptr, nullptr, nullptr), &EVP_PKEY_free);
304 if (key == nullptr) {
305 if (filename.has_value()) {
306 throw runtime_error(getName() + ": Failed to read private key from PEM file `" + filename->get() + "`");
307 }
308
309 throw runtime_error(getName() + ": Failed to read private key from PEM contents");
310 }
311
312 std::size_t keylen = DECAF_EDDSA_448_PRIVATE_BYTES;
313 int ret = EVP_PKEY_get_raw_private_key(key.get(), d_seckey, &keylen);
314 if (ret == 0) {
315 if (filename.has_value()) {
316 throw runtime_error(getName() + ": Failed to get private key from PEM file contents `" + filename->get() + "`");
317 }
318
319 throw runtime_error(getName() + ": Failed to get private key from PEM contents");
320 }
321
322 keylen = DECAF_EDDSA_448_PUBLIC_BYTES;
323 ret = EVP_PKEY_get_raw_public_key(key.get(), d_pubkey, &keylen);
324 if (ret == 0) {
325 if (filename.has_value()) {
326 throw runtime_error(getName() + ": Failed to get public key from PEM file contents `" + filename->get() + "`");
327 }
328
329 throw runtime_error(getName() + ": Failed to get public key from PEM contents");
330 }
331 }
332
333 void DecafED448DNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
334 {
335 auto key = std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)>(EVP_PKEY_new_raw_private_key(EVP_PKEY_ED448, nullptr, d_seckey, DECAF_EDDSA_448_PRIVATE_BYTES), EVP_PKEY_free);
336 if (key == nullptr) {
337 throw runtime_error(getName() + ": Could not create private key from buffer");
338 }
339
340 auto ret = PEM_write_PrivateKey(&outputFile, key.get(), nullptr, nullptr, 0, nullptr, nullptr);
341 if (ret == 0) {
342 throw runtime_error(getName() + ": Could not convert private key to PEM");
343 }
344 }
345 #endif
346
347 int DecafED448DNSCryptoKeyEngine::getBits() const
348 {
349 return DECAF_EDDSA_448_PRIVATE_BYTES << 3;
350 }
351
352 DNSCryptoKeyEngine::storvector_t DecafED448DNSCryptoKeyEngine::convertToISCVector() const
353 {
354 /*
355 Private-key-format: v1.2
356 Algorithm: 16 (ED448)
357 PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA
358 */
359
360 auto storvector = storvector_t{
361 {"Algorithm", "16 (ED448)"},
362 {"PrivateKey", string((char*)d_seckey, DECAF_EDDSA_448_PRIVATE_BYTES)},
363 };
364
365 return storvector;
366 }
367
368 void DecafED448DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
369 {
370 /*
371 Private-key-format: v1.2
372 Algorithm: 16 (ED448)
373 PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA
374 */
375
376 pdns::checked_stoi_into(drc.d_algorithm, stormap["algorithm"]);
377 string privateKey = stormap["privatekey"];
378
379 if (privateKey.length() != DECAF_EDDSA_448_PRIVATE_BYTES)
380 throw runtime_error("Private key size mismatch in ISCMap, DecafED448 class");
381
382 typename EdDSA<Ed448Goldilocks>::PrivateKey priv(Block((const unsigned char*)privateKey.c_str(), DECAF_EDDSA_448_PRIVATE_BYTES));
383 typename EdDSA<Ed448Goldilocks>::PublicKey pub(priv);
384
385 priv.serialize_into(d_seckey);
386 pub.serialize_into(d_pubkey);
387 }
388
389 std::string DecafED448DNSCryptoKeyEngine::getPublicKeyString() const
390 {
391 return string((char*)d_pubkey, DECAF_EDDSA_448_PUBLIC_BYTES);
392 }
393
394 void DecafED448DNSCryptoKeyEngine::fromPublicKeyString(const std::string& input)
395 {
396 if (input.length() != DECAF_EDDSA_448_PUBLIC_BYTES)
397 throw runtime_error("Public key size mismatch, DecafED448 class");
398
399 memcpy(d_pubkey, input.c_str(), DECAF_EDDSA_448_PUBLIC_BYTES);
400 }
401
402 std::string DecafED448DNSCryptoKeyEngine::sign(const std::string& msg) const
403 {
404 typename EdDSA<Ed448Goldilocks>::PrivateKey priv(Block(d_seckey, DECAF_EDDSA_448_PRIVATE_BYTES));
405
406 SecureBuffer message(msg.begin(), msg.end());
407
408 SecureBuffer sig = priv.sign(message);
409
410 return string(sig.begin(), sig.end());
411 }
412
413 bool DecafED448DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const
414 {
415 if (signature.length() != DECAF_EDDSA_448_SIGNATURE_BYTES)
416 return false;
417
418 typename EdDSA<Ed448Goldilocks>::PublicKey pub(Block(d_pubkey, DECAF_EDDSA_448_PUBLIC_BYTES));
419
420 SecureBuffer sig(signature.begin(), signature.end());
421 SecureBuffer message(msg.begin(), msg.end());
422
423 try {
424 pub.verify(sig, message);
425 }
426 catch (const CryptoException& e) {
427 return false;
428 }
429
430 return true;
431 }
432
433 namespace
434 {
435 const struct LoaderDecafStruct
436 {
437 LoaderDecafStruct()
438 {
439 DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &DecafED25519DNSCryptoKeyEngine::maker, true);
440 DNSCryptoKeyEngine::report(DNSSECKeeper::ED448, &DecafED448DNSCryptoKeyEngine::maker);
441 }
442 } loaderdecaf;
443 }