]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/sodiumsigners.cc
rec: mention rust compiler in compiling docs
[thirdparty/pdns.git] / pdns / sodiumsigners.cc
1 #include <openssl/evp.h>
2 #include <openssl/pem.h>
3 extern "C"
4 {
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif
8 #include <sodium.h>
9 }
10 #include "dnssecinfra.hh"
11 #include "dnsseckeeper.hh"
12
13 class SodiumED25519DNSCryptoKeyEngine : public DNSCryptoKeyEngine
14 {
15 public:
16 explicit SodiumED25519DNSCryptoKeyEngine(unsigned int algo) :
17 DNSCryptoKeyEngine(algo)
18 {}
19 string getName() const override { return "Sodium ED25519"; }
20 void create(unsigned int bits) override;
21
22 #if defined(HAVE_LIBCRYPTO_ED25519)
23 /**
24 * \brief Creates an ED25519 key engine from a PEM file.
25 *
26 * Receives an open file handle with PEM contents and creates an ED25519 key engine.
27 *
28 * \param[in] drc Key record contents to be populated.
29 *
30 * \param[in] inputFile An open file handle to a file containing ED25519 PEM contents.
31 *
32 * \param[in] filename Only used for providing filename information in error messages.
33 *
34 * \return An ED25519 key engine populated with the contents of the PEM file.
35 */
36 void createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename = std::nullopt) override;
37
38 /**
39 * \brief Writes this key's contents to a file.
40 *
41 * Receives an open file handle and writes this key's contents to the
42 * file.
43 *
44 * \param[in] outputFile An open file handle for writing.
45 *
46 * \exception std::runtime_error In case of OpenSSL errors.
47 */
48 void convertToPEMFile(std::FILE& outputFile) const override;
49 #endif
50
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;
58
59 static std::unique_ptr<DNSCryptoKeyEngine> maker(unsigned int algorithm)
60 {
61 return make_unique<SodiumED25519DNSCryptoKeyEngine>(algorithm);
62 }
63
64 private:
65 unsigned char d_pubkey[crypto_sign_ed25519_PUBLICKEYBYTES];
66 unsigned char d_seckey[crypto_sign_ed25519_SECRETKEYBYTES];
67 };
68
69 void SodiumED25519DNSCryptoKeyEngine::create(unsigned int bits)
70 {
71 if (bits != crypto_sign_ed25519_SEEDBYTES * 8) {
72 throw runtime_error("Unsupported key length of " + std::to_string(bits) + " bits requested, SodiumED25519 class");
73 }
74 crypto_sign_ed25519_keypair(d_pubkey, d_seckey);
75 }
76
77 #if defined(HAVE_LIBCRYPTO_ED25519)
78 void SodiumED25519DNSCryptoKeyEngine::createFromPEMFile(DNSKEYRecordContent& drc, std::FILE& inputFile, std::optional<std::reference_wrapper<const std::string>> filename)
79 {
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);
82 if (key == nullptr) {
83 if (filename.has_value()) {
84 throw runtime_error(getName() + ": Failed to read private key from PEM file `" + filename->get() + "`");
85 }
86
87 throw runtime_error(getName() + ": Failed to read private key from PEM contents");
88 }
89
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);
94 if (ret == 0) {
95 if (filename.has_value()) {
96 throw runtime_error(getName() + ": Failed to get private key from PEM file contents `" + filename->get() + "`");
97 }
98
99 throw runtime_error(getName() + ": Failed to get private key from PEM contents");
100 }
101
102 std::size_t pubKeyLen = crypto_sign_ed25519_PUBLICKEYBYTES;
103 ret = EVP_PKEY_get_raw_public_key(key.get(), d_pubkey, &pubKeyLen);
104 if (ret == 0) {
105 if (filename.has_value()) {
106 throw runtime_error(getName() + ": Failed to get public key from PEM file contents `" + filename->get() + "`");
107 }
108
109 throw runtime_error(getName() + ": Failed to get public key from PEM contents");
110 }
111
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);
115 }
116
117 void SodiumED25519DNSCryptoKeyEngine::convertToPEMFile(std::FILE& outputFile) const
118 {
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");
122 }
123
124 auto ret = PEM_write_PrivateKey(&outputFile, key.get(), nullptr, nullptr, 0, nullptr, nullptr);
125 if (ret == 0) {
126 throw runtime_error(getName() + ": Could not convert private key to PEM");
127 }
128 }
129 #endif
130
131 int SodiumED25519DNSCryptoKeyEngine::getBits() const
132 {
133 return crypto_sign_ed25519_SEEDBYTES * 8;
134 }
135
136 DNSCryptoKeyEngine::storvector_t SodiumED25519DNSCryptoKeyEngine::convertToISCVector() const
137 {
138 /*
139 Private-key-format: v1.2
140 Algorithm: 15 (ED25519)
141 PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=
142 */
143
144 storvector_t storvector;
145 string algorithm = "15 (ED25519)";
146
147 storvector.emplace_back("Algorithm", algorithm);
148
149 storvector.emplace_back("PrivateKey", string((char*)d_seckey, crypto_sign_ed25519_SEEDBYTES));
150 return storvector;
151 }
152
153 void SodiumED25519DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
154 {
155 /*
156 Private-key-format: v1.2
157 Algorithm: 15 (ED25519)
158 PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=
159 */
160
161 pdns::checked_stoi_into(drc.d_algorithm, stormap["algorithm"]);
162 string privateKey = stormap["privatekey"];
163
164 if (privateKey.length() != crypto_sign_ed25519_SEEDBYTES)
165 throw runtime_error("Seed size mismatch in ISCMap, SodiumED25519 class");
166
167 auto seed = std::make_unique<unsigned char[]>(crypto_sign_ed25519_SEEDBYTES);
168
169 memcpy(seed.get(), privateKey.c_str(), crypto_sign_ed25519_SEEDBYTES);
170 crypto_sign_ed25519_seed_keypair(d_pubkey, d_seckey, seed.get());
171 }
172
173 std::string SodiumED25519DNSCryptoKeyEngine::getPublicKeyString() const
174 {
175 return string((char*)d_pubkey, crypto_sign_ed25519_PUBLICKEYBYTES);
176 }
177
178 void SodiumED25519DNSCryptoKeyEngine::fromPublicKeyString(const std::string& input)
179 {
180 if (input.length() != crypto_sign_ed25519_PUBLICKEYBYTES)
181 throw runtime_error("Public key size mismatch, SodiumED25519 class");
182
183 memcpy(d_pubkey, input.c_str(), crypto_sign_ed25519_PUBLICKEYBYTES);
184 }
185
186 std::string SodiumED25519DNSCryptoKeyEngine::sign(const std::string& msg) const
187 {
188 unsigned char signature[crypto_sign_ed25519_BYTES];
189
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);
194
195 return {(const char*)signature, crypto_sign_ed25519_BYTES};
196 }
197
198 bool SodiumED25519DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const
199 {
200 if (signature.length() != crypto_sign_ed25519_BYTES) {
201 return false;
202 }
203
204 return crypto_sign_ed25519_verify_detached((const unsigned char*)signature.c_str(), (const unsigned char*)msg.c_str(), msg.length(), d_pubkey) == 0;
205 }
206
207 namespace
208 {
209 const struct LoaderSodiumStruct
210 {
211 LoaderSodiumStruct()
212 {
213 DNSCryptoKeyEngine::report(DNSSECKeeper::ED25519, &SodiumED25519DNSCryptoKeyEngine::maker);
214 }
215 } loadersodium;
216 }