]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
f7b9d3b7 | 25 | #include <functional> |
4691b2df BH |
26 | #include "dnsparser.hh" |
27 | #include "sstuff.hh" | |
28 | #include "misc.hh" | |
29 | #include "dnswriter.hh" | |
30 | #include "dnsrecords.hh" | |
acbfb1c1 | 31 | #ifndef RECURSOR |
4691b2df | 32 | #include "statbag.hh" |
acbfb1c1 | 33 | #endif |
4691b2df | 34 | #include "iputils.hh" |
fa8fd4d2 | 35 | |
4691b2df | 36 | #include <boost/algorithm/string.hpp> |
1e05b07c | 37 | #include "dnssecinfra.hh" |
4691b2df | 38 | #include "dnsseckeeper.hh" |
5e8e902d CH |
39 | #include <openssl/hmac.h> |
40 | #include <openssl/sha.h> | |
673208a2 BH |
41 | #include <boost/assign/std/vector.hpp> // for 'operator+=()' |
42 | #include <boost/assign/list_inserter.hpp> | |
f309dacd | 43 | #include "base64.hh" |
9e04108d | 44 | #include "namespaces.hh" |
8daea594 AT |
45 | #ifdef HAVE_P11KIT1 |
46 | #include "pkcs11signers.hh" | |
47 | #endif | |
b08f1315 | 48 | #include "gss_context.hh" |
da15912b | 49 | #include "misc.hh" |
8daea594 | 50 | |
673208a2 BH |
51 | using namespace boost::assign; |
52 | ||
a2c6e554 | 53 | std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc, const char* fname) |
4691b2df | 54 | { |
f309dacd | 55 | string sline, isc; |
5e1f23ca | 56 | auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname, "r"), fclose); |
699e6e37 BH |
57 | if(!fp) { |
58 | throw runtime_error("Unable to read file '"+string(fname)+"' for generating DNS Private Key"); | |
59 | } | |
1e05b07c | 60 | |
5e1f23ca | 61 | while(stringfgets(fp.get(), sline)) { |
f309dacd | 62 | isc += sline; |
699e6e37 | 63 | } |
5e1f23ca RG |
64 | fp.reset(); |
65 | ||
a2c6e554 | 66 | auto dke = makeFromISCString(drc, isc); |
f7b9d3b7 | 67 | auto checkKeyErrors = std::vector<std::string>{}; |
be135eeb | 68 | |
f7b9d3b7 | 69 | if(!dke->checkKey(checkKeyErrors)) { |
be135eeb | 70 | string reason; |
f7b9d3b7 | 71 | if(!checkKeyErrors.empty()) { |
be135eeb PD |
72 | reason = " ("+boost::algorithm::join(checkKeyErrors, ", ")+")"; |
73 | } | |
74 | throw runtime_error("Invalid DNS Private Key in file '"+string(fname)+"'"+reason); | |
8ca3ea33 RG |
75 | } |
76 | return dke; | |
f309dacd | 77 | } |
4691b2df | 78 | |
a2c6e554 | 79 | std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& drc, const std::string& content) |
f309dacd | 80 | { |
3b9e71ab RG |
81 | enum class KeyTypes : uint8_t { str, numeric, base64 }; |
82 | const std::map<std::string, KeyTypes> knownKeys = { | |
83 | { "algorithm", KeyTypes::numeric }, | |
84 | { "modulus", KeyTypes::base64 }, | |
85 | { "publicexponent", KeyTypes::base64 }, | |
86 | { "privateexponent", KeyTypes::base64 }, | |
87 | { "prime1", KeyTypes::base64 }, | |
88 | { "prime2", KeyTypes::base64 }, | |
89 | { "exponent1", KeyTypes::base64 }, | |
90 | { "exponent2", KeyTypes::base64 }, | |
91 | { "coefficient", KeyTypes::base64 }, | |
92 | { "privatekey", KeyTypes::base64 }, | |
93 | { "engine", KeyTypes::str }, | |
94 | { "slot", KeyTypes::str }, | |
95 | { "pin", KeyTypes::str }, | |
96 | { "label", KeyTypes::str }, | |
97 | { "publabel", KeyTypes::str }, | |
98 | { "private-key-format", KeyTypes::str }, | |
99 | { "flags", KeyTypes::numeric } | |
100 | }; | |
101 | unsigned int algorithm = 0; | |
f309dacd | 102 | string sline, key, value, raw; |
9e04108d | 103 | std::istringstream str(content); |
f309dacd | 104 | map<string, string> stormap; |
495155bf | 105 | |
3b9e71ab | 106 | while (std::getline(str, sline)) { |
1a09e47b | 107 | std::tie(key,value) = splitField(sline, ':'); |
dc593046 | 108 | boost::trim(value); |
3b9e71ab RG |
109 | |
110 | toLowerInPlace(key); | |
111 | const auto it = knownKeys.find(key); | |
112 | if (it != knownKeys.cend()) { | |
113 | if (it->second == KeyTypes::str) { | |
114 | stormap[key] = value; | |
115 | } | |
116 | else if (it->second == KeyTypes::base64) { | |
117 | try { | |
118 | raw.clear(); | |
119 | B64Decode(value, raw); | |
120 | stormap[key] = raw; | |
121 | } | |
122 | catch (const std::exception& e) { | |
123 | throw std::runtime_error("Error while trying to base64 decode the value of the '" + key + "' key from the ISC map: " + e.what()); | |
124 | } | |
125 | } | |
126 | else if (it->second == KeyTypes::numeric) { | |
127 | try { | |
a0383aad | 128 | auto num = pdns::checked_stoi<unsigned int>(value); |
3b9e71ab RG |
129 | stormap[key] = std::to_string(num); |
130 | if (key == "algorithm") { | |
131 | algorithm = num; | |
132 | } | |
133 | } | |
134 | catch (const std::exception& e) { | |
1c83d0da | 135 | throw std::runtime_error("Error while trying to parse the numeric value of the '" + key + "' key from the ISC map: " + e.what()); |
3b9e71ab RG |
136 | } |
137 | } | |
138 | } | |
139 | else { | |
140 | try { | |
141 | raw.clear(); | |
142 | B64Decode(value, raw); | |
143 | stormap[key] = raw; | |
144 | } | |
145 | catch (const std::exception& e) { | |
146 | stormap[key] = value; | |
147 | } | |
f309dacd | 148 | } |
f309dacd | 149 | } |
3b9e71ab | 150 | |
a2c6e554 | 151 | std::unique_ptr<DNSCryptoKeyEngine> dpk; |
8daea594 | 152 | |
3b9e71ab | 153 | if (stormap.count("engine")) { |
8daea594 | 154 | #ifdef HAVE_P11KIT1 |
3b9e71ab | 155 | if (stormap.count("slot") == 0) { |
f0a0745b | 156 | throw PDNSException("Cannot load PKCS#11 key, no Slot specified"); |
3b9e71ab | 157 | } |
f0a0745b | 158 | // we need PIN to be at least empty |
3b9e71ab RG |
159 | if (stormap.count("pin") == 0) { |
160 | stormap["pin"] = ""; | |
161 | } | |
1e05b07c | 162 | dpk = PKCS11DNSCryptoKeyEngine::maker(algorithm); |
8daea594 | 163 | #else |
70f0f8c4 | 164 | throw PDNSException("Cannot load PKCS#11 key without support for it"); |
8daea594 AT |
165 | #endif |
166 | } else { | |
a2c6e554 | 167 | dpk = make(algorithm); |
8daea594 | 168 | } |
f309dacd | 169 | dpk->fromISCMap(drc, stormap); |
022e5e0b BH |
170 | return dpk; |
171 | } | |
172 | ||
e959b65f | 173 | std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMFile(DNSKEYRecordContent& drc, const uint8_t algorithm, std::FILE& inputFile, const std::string& filename) |
06cb6907 | 174 | { |
87066241 | 175 | auto maker = DNSCryptoKeyEngine::make(algorithm); |
0186c941 | 176 | maker->createFromPEMFile(drc, inputFile, filename); |
87066241 | 177 | return maker; |
06cb6907 FM |
178 | } |
179 | ||
3de36d82 FM |
180 | std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecordContent& drc, uint8_t algorithm, const std::string& contents) |
181 | { | |
182 | auto maker = DNSCryptoKeyEngine::make(algorithm); | |
183 | maker->createFromPEMString(drc, contents); | |
184 | return maker; | |
185 | } | |
186 | ||
189bb9d2 BH |
187 | std::string DNSCryptoKeyEngine::convertToISC() const |
188 | { | |
d35497e5 | 189 | storvector_t storvector = this->convertToISCVector(); |
189bb9d2 | 190 | ostringstream ret; |
fdb8aa96 FM |
191 | ret << "Private-key-format: v1.2\n"; |
192 | for (const storvector_t::value_type& value : storvector) { | |
193 | // clang-format off | |
1e05b07c | 194 | if(value.first != "Algorithm" && value.first != "PIN" && |
8daea594 | 195 | value.first != "Slot" && value.first != "Engine" && |
fdb8aa96 FM |
196 | value.first != "Label" && value.first != "PubLabel") { |
197 | ret << value.first << ": " << Base64Encode(value.second) << "\n"; | |
198 | } | |
199 | else { | |
200 | ret << value.first << ": " << value.second << "\n"; | |
201 | } | |
202 | // clang-format on | |
189bb9d2 BH |
203 | } |
204 | return ret.str(); | |
205 | } | |
f309dacd | 206 | |
a2c6e554 | 207 | std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::make(unsigned int algo) |
022e5e0b | 208 | { |
8455425c | 209 | const makers_t& makers = getMakers(); |
926eb28c FM |
210 | |
211 | auto iter = makers.find(algo); | |
0eadf4db | 212 | if (iter != makers.cend()) { |
022e5e0b | 213 | return (iter->second)(algo); |
0eadf4db | 214 | } |
926eb28c FM |
215 | |
216 | throw runtime_error("Request to create key object for unknown algorithm number " + std::to_string(algo)); | |
699e6e37 | 217 | } |
4691b2df | 218 | |
98d13a90 PL |
219 | /** |
220 | * Returns the supported DNSSEC algorithms with the name of the Crypto Backend used | |
221 | * | |
222 | * @return A vector with pairs of (algorithm-number (int), backend-name (string)) | |
223 | */ | |
224 | vector<pair<uint8_t, string>> DNSCryptoKeyEngine::listAllAlgosWithBackend() | |
225 | { | |
226 | vector<pair<uint8_t, string>> ret; | |
227 | for (auto const& value : getMakers()) { | |
a2c6e554 | 228 | auto dcke = value.second(value.first); |
e32a8d46 | 229 | ret.emplace_back(value.first, dcke->getName()); |
98d13a90 PL |
230 | } |
231 | return ret; | |
232 | } | |
233 | ||
a47ea8ec OM |
234 | string DNSCryptoKeyEngine::listSupportedAlgoNames() |
235 | { | |
236 | set<unsigned int> algos; | |
237 | auto pairs = DNSCryptoKeyEngine::listAllAlgosWithBackend(); | |
238 | for (const auto& pair : pairs) { | |
239 | algos.insert(pair.first); | |
240 | } | |
241 | string ret; | |
242 | bool first = true; | |
243 | for (auto algo : algos) { | |
244 | if (!first) { | |
245 | ret.append(" "); | |
246 | } | |
247 | else { | |
248 | first = false; | |
249 | } | |
250 | ret.append(DNSSECKeeper::algorithm2name(algo)); | |
251 | if (isAlgorithmSwitchedOff(algo)) { | |
252 | ret.append("(disabled)"); | |
253 | } | |
254 | } | |
255 | ret.append("\n"); | |
256 | return ret; | |
257 | } | |
258 | ||
8d9f38f2 | 259 | void DNSCryptoKeyEngine::report(unsigned int algo, maker_t* maker, bool fallback) |
022e5e0b | 260 | { |
189bb9d2 | 261 | getAllMakers()[algo].push_back(maker); |
d2e1a4e0 | 262 | if (getMakers().count(algo) != 0 && fallback) { |
f309dacd | 263 | return; |
4691b2df | 264 | } |
2c281453 | 265 | getMakers()[algo] = maker; |
699e6e37 BH |
266 | } |
267 | ||
166d8647 | 268 | bool DNSCryptoKeyEngine::testAll() |
189bb9d2 | 269 | { |
166d8647 KM |
270 | bool ret=true; |
271 | ||
ef7cd021 | 272 | for(const allmakers_t::value_type& value : getAllMakers()) |
189bb9d2 | 273 | { |
ef7cd021 | 274 | for(maker_t* creator : value.second) { |
530b4335 | 275 | |
ef7cd021 | 276 | for(maker_t* signer : value.second) { |
530b4335 | 277 | // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier; |
1e05b07c | 278 | |
ef7cd021 | 279 | for(maker_t* verifier : value.second) { |
530b4335 | 280 | try { |
f558c987 | 281 | testMakers(value.first, creator, signer, verifier); |
530b4335 PD |
282 | } |
283 | catch(std::exception& e) | |
284 | { | |
285 | cerr<<e.what()<<endl; | |
166d8647 | 286 | ret=false; |
530b4335 | 287 | } |
189bb9d2 BH |
288 | } |
289 | } | |
290 | } | |
291 | } | |
166d8647 | 292 | return ret; |
189bb9d2 BH |
293 | } |
294 | ||
166d8647 | 295 | bool DNSCryptoKeyEngine::testOne(int algo) |
cbb0025b | 296 | { |
166d8647 KM |
297 | bool ret=true; |
298 | ||
ef7cd021 | 299 | for(maker_t* creator : getAllMakers()[algo]) { |
530b4335 | 300 | |
ef7cd021 | 301 | for(maker_t* signer : getAllMakers()[algo]) { |
cbb0025b PD |
302 | // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier; |
303 | ||
ef7cd021 | 304 | for(maker_t* verifier : getAllMakers()[algo]) { |
cbb0025b | 305 | try { |
f558c987 | 306 | testMakers(algo, creator, signer, verifier); |
cbb0025b PD |
307 | } |
308 | catch(std::exception& e) | |
309 | { | |
310 | cerr<<e.what()<<endl; | |
166d8647 | 311 | ret=false; |
cbb0025b PD |
312 | } |
313 | } | |
314 | } | |
530b4335 | 315 | } |
166d8647 | 316 | return ret; |
cbb0025b | 317 | } |
f558c987 | 318 | |
a2458070 | 319 | static map<string, string> ISCStringtoMap(const string& argStr) |
81a94207 OM |
320 | { |
321 | unsigned int algorithm = 0; | |
322 | string sline; | |
323 | string key; | |
324 | string value; | |
325 | string raw; | |
326 | std::istringstream str(argStr); | |
327 | map<string, string> stormap; | |
328 | ||
329 | while(std::getline(str, sline)) { | |
330 | std::tie(key,value)=splitField(sline, ':'); | |
331 | boost::trim(value); | |
332 | if(pdns_iequals(key,"algorithm")) { | |
333 | pdns::checked_stoi_into(algorithm, value); | |
334 | stormap["algorithm"] = std::to_string(algorithm); | |
335 | continue; | |
336 | } | |
337 | if (pdns_iequals(key,"pin")) { | |
338 | stormap["pin"] = value; | |
339 | continue; | |
340 | } | |
341 | if (pdns_iequals(key,"engine")) { | |
342 | stormap["engine"] = value; | |
343 | continue; | |
344 | } | |
345 | if (pdns_iequals(key,"slot")) { | |
346 | int slot = std::stoi(value); | |
347 | stormap["slot"]=std::to_string(slot); | |
348 | continue; | |
349 | } | |
350 | if (pdns_iequals(key,"label")) { | |
351 | stormap["label"] = value; | |
352 | continue; | |
353 | } | |
354 | if(pdns_iequals(key, "Private-key-format")) { | |
355 | continue; | |
356 | } | |
357 | raw.clear(); | |
358 | B64Decode(value, raw); | |
359 | stormap[toLower(key)] = raw; | |
360 | } | |
361 | return stormap; | |
362 | } | |
363 | ||
a2458070 | 364 | bool DNSCryptoKeyEngine::testVerify(unsigned int algo, maker_t* verifier) |
81a94207 OM |
365 | { |
366 | const string message("Hi! How is life?"); | |
367 | const string pubkey5 = "AwEAAe2srzo8UfPx5WwoRXTRdo0H8U4iYW6qneronwKlRtXrpOqgZWPtYGVZl1Q7JXqbxxH9aVK5iK6aYOVfxbwwGHejaY0NraqrxL60F5FhHGHg+zox1en8kEX2TcQHxoZaiK1iUgPkMrHJlX5yI5+p2V4qap5VPQsR/WfeFVudNsBEF/XRvg0Exh65fPI/e8sYNgAiflzdN9/5RM644r6viBdieuwUNwEV2HPizCBMssYzx2F29CqNseToqCKQlj1tghuGAsiiSKeosfDLlRPDe/uxtij0wqe0FNybj1oL3OG8Lq3xp8yXIG4CF59xmRDKdnGDmVycKzUWkVOZpesCsUU="; | |
368 | const string sig5 = "nMnMakbQiiCKIYsEiv4R75+8wvjQav2LPGIKucbqUZUz5sy1ovc2Pp7JVcOuyVyzQu5XH+CetDnTlqiEJWFHNU1jqEwwFK83GVOLABtvXSOvgmGwZGnHOouAchkrzgSSBoEh3+UUN3OsFZA21q6TZVRJBNBm7Ch/PxqSBkFS46ko/qLAUJ1p7/ymzwGNhuOfguHO3dAJ+LgcrNGLZQFDJ1aqT3kZ7LtXX2CQdd7EXgUs6VkE4Z3JN1RmPTk8kAJdZ4JLUR6lgu1dRlSPLGzqv+5d1yI7+h+B0LFNuDdQblDlBstO3LEs1KSaQld+TqVExpjj87oEg6wL/G/XOGabmQ=="; | |
369 | ||
370 | const string pubkey7 = "AwEAAc4n7xPG6yJe6YAsg6oQ+7YjbL7wuDLCP4juOSaDsst2Mehc5eYdT7xJT2H9foTIq7ABkkp8Er1Bh6gDzB/0xvArARdH6DS3P5pUP6w5Zoz4Gu79y3pP6IsR3ZyhiQRSnht1ElnIGZzb1zpi7Y4Y8LZ18NYN2qdLasXx/h6hpRjdcF1s7svZKvfJdvCSgDHHD/JFtDGSOn6qt6i5UFSrObxMUMWbxfOsnqr/eXUQcF/aePdqDXO47yDaSH8sFZoglgvEDiOIkky9DV5VKamvVW8anxE5Vv7y4EPpZKXB3CgUW+NvaoasdgYPFmGM4EcnXh2EFFnSPDL6iwDubiL7s2k="; | |
371 | const string sig7 = "B04Oqmh/nF6BybBGsInauTXH6nlW3VhT2PeSzXVaxQ42QsbbXUgIKuzp2/R7diiEBzbbQ3Eg5vtHOKfEQDkArmOR1oU6yIkyrKHsJkpCvclCyaFiJXrwxkH+A2y8vB+loeDMJKJVwjn7fH9zwBI3Mk7SFuOgYXgzBUNhb5DeQ9RzRbxMcpSc8Cgtjn+QpmTNgL6olpBNsStYz9bSLXBk1EGhmZeBYhliw/2Fse75OoRxIuufKiN6sAD5bKQxp73QQUU+yunVuSeHJizNct8b4f9RXFe49wtZWt5rB0oYXG6zUv0Dq7xJHpUq6v1eB2wf2NucftCKwWu18r4TxkVC5A=="; | |
372 | ||
373 | string b64pubkey; | |
374 | string b64sig; | |
375 | switch (algo) { | |
376 | case DNSSECKeeper::RSASHA1: | |
377 | b64pubkey = pubkey5; | |
378 | b64sig = sig5; | |
379 | break; | |
380 | case DNSSECKeeper::RSASHA1NSEC3SHA1: | |
381 | b64pubkey = pubkey7; | |
382 | b64sig = sig7; | |
383 | break; | |
384 | default: | |
385 | throw runtime_error("Verification of verifier called for unimplemented case"); | |
386 | } | |
387 | ||
388 | string pubkey; | |
389 | string sig; | |
390 | B64Decode(b64pubkey, pubkey); | |
391 | B64Decode(b64sig, sig); | |
392 | auto dckeVerify = verifier(algo); | |
393 | dckeVerify->fromPublicKeyString(pubkey); | |
394 | ||
395 | auto ret = dckeVerify->verify(message, sig); | |
a2458070 | 396 | return ret; |
81a94207 OM |
397 | } |
398 | ||
399 | bool DNSCryptoKeyEngine::verifyOne(unsigned int algo) | |
400 | { | |
891f1737 OM |
401 | const auto& makers = getAllMakers(); |
402 | auto iter = makers.find(algo); | |
da6a2d87 | 403 | // No algo found |
891f1737 OM |
404 | if (iter == makers.cend()) { |
405 | return false; | |
406 | } | |
407 | // Algo found, but maker empty? Should not happen | |
408 | if (iter->second.empty()) { | |
409 | return false; | |
410 | } | |
411 | // Check that all maker->verify return true | |
412 | return std::all_of(iter->second.begin(), iter->second.end(), [algo](maker_t* verifier) { | |
81a94207 | 413 | try { |
891f1737 OM |
414 | if (!testVerify(algo, verifier)) { |
415 | return false; | |
416 | } | |
81a94207 OM |
417 | } |
418 | catch (std::exception& e) { | |
891f1737 | 419 | return false; |
81a94207 | 420 | } |
891f1737 OM |
421 | return true; |
422 | }); | |
81a94207 OM |
423 | } |
424 | ||
f558c987 | 425 | void DNSCryptoKeyEngine::testMakers(unsigned int algo, maker_t* creator, maker_t* signer, maker_t* verifier) |
189bb9d2 | 426 | { |
a2c6e554 RG |
427 | auto dckeCreate = creator(algo); |
428 | auto dckeSign = signer(algo); | |
429 | auto dckeVerify = verifier(algo); | |
530b4335 | 430 | |
66afe5d4 | 431 | cout<<"Testing algorithm "<<algo<<"("<<DNSSECKeeper::algorithm2name(algo)<<"): '"<<dckeCreate->getName()<<"' ->'"<<dckeSign->getName()<<"' -> '"<<dckeVerify->getName()<<"' "; |
189bb9d2 BH |
432 | unsigned int bits; |
433 | if(algo <= 10) | |
cc835c12 | 434 | bits=2048; |
902c4e9c CH |
435 | else if(algo == DNSSECKeeper::ECCGOST || algo == DNSSECKeeper::ECDSA256 || algo == DNSSECKeeper::ED25519) |
436 | bits = 256; | |
437 | else if(algo == DNSSECKeeper::ECDSA384) | |
45826dd7 | 438 | bits = 384; |
902c4e9c | 439 | else if(algo == DNSSECKeeper::ED448) |
21a8834a | 440 | bits = 456; |
45826dd7 | 441 | else |
335da0ba | 442 | throw runtime_error("Can't guess key size for algorithm "+std::to_string(algo)); |
45826dd7 | 443 | |
f558c987 PD |
444 | DTime dt; dt.set(); |
445 | for(unsigned int n = 0; n < 100; ++n) | |
446 | dckeCreate->create(bits); | |
66afe5d4 | 447 | cout<<"("<<dckeCreate->getBits()<<" bits) "; |
f558c987 | 448 | unsigned int udiffCreate = dt.udiff() / 100; |
530b4335 | 449 | |
81a94207 | 450 | { |
530b4335 | 451 | DNSKEYRecordContent dkrc; |
a2458070 | 452 | auto stormap = ISCStringtoMap(dckeCreate->convertToISC()); |
81a94207 | 453 | |
530b4335 | 454 | dckeSign->fromISCMap(dkrc, stormap); |
45c2bc60 RG |
455 | if(!dckeSign->checkKey()) { |
456 | throw runtime_error("Verification of key with creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed"); | |
8ca3ea33 | 457 | } |
530b4335 PD |
458 | } |
459 | ||
189bb9d2 | 460 | string message("Hi! How is life?"); |
1e05b07c | 461 | |
189bb9d2 | 462 | string signature; |
f558c987 | 463 | dt.set(); |
f011d0ad BH |
464 | for(unsigned int n = 0; n < 100; ++n) |
465 | signature = dckeSign->sign(message); | |
466 | unsigned int udiffSign= dt.udiff()/100, udiffVerify; | |
1e05b07c | 467 | |
189bb9d2 | 468 | dckeVerify->fromPublicKeyString(dckeSign->getPublicKeyString()); |
7fbe4163 CH |
469 | if (dckeVerify->getPublicKeyString().compare(dckeSign->getPublicKeyString())) { |
470 | throw runtime_error("Comparison of public key loaded into verifier produced by signer failed"); | |
471 | } | |
189bb9d2 | 472 | dt.set(); |
f558c987 PD |
473 | bool verified; |
474 | for(unsigned int n = 0; n < 100; ++n) | |
475 | verified = dckeVerify->verify(message, signature); | |
476 | ||
477 | if(verified) { | |
478 | udiffVerify = dt.udiff() / 100; | |
35f4f574 | 479 | cout<<"Signature & verify ok, create "<<udiffCreate<<"us, signature "<<udiffSign<<"us, verify "<<udiffVerify<<"us"<<endl; |
189bb9d2 BH |
480 | } |
481 | else { | |
530b4335 | 482 | throw runtime_error("Verification of creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed"); |
189bb9d2 | 483 | } |
189bb9d2 BH |
484 | } |
485 | ||
a2c6e554 | 486 | std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPublicKeyString(unsigned int algorithm, const std::string& content) |
aa65a832 | 487 | { |
a2c6e554 | 488 | auto dpk = make(algorithm); |
189bb9d2 | 489 | dpk->fromPublicKeyString(content); |
aa65a832 BH |
490 | return dpk; |
491 | } | |
492 | ||
125058a0 PL |
493 | /** |
494 | * Returns the string that should be hashed to create/verify the RRSIG content | |
495 | * | |
496 | * @param qname DNSName of the RRSIG's owner name. | |
497 | * @param rrc The RRSIGRecordContent we take the Type Covered and | |
498 | * original TTL fields from. | |
499 | * @param signRecords A vector of DNSRecordContent shared_ptr's that are covered | |
500 | * by the RRSIG, where we get the RDATA from. | |
501 | * @param processRRSIGLabels A boolean to trigger processing the RRSIG's "Labels" | |
502 | * field. This is usually only needed for validation | |
503 | * purposes, as the authoritative server correctly | |
504 | * sets qname to the wildcard. | |
505 | */ | |
e7ea7d4e | 506 | string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, const sortedRecords_t& signRecords, bool processRRSIGLabels, bool includeRRSIG_RDATA) |
4691b2df | 507 | { |
4691b2df | 508 | string toHash; |
4691b2df | 509 | |
e7ea7d4e O |
510 | // dnssec: signature = sign(RRSIG_RDATA | RR(1) | RR(2)... ) |
511 | // From RFC 4034 | |
512 | // RRSIG_RDATA is the wire format of the RRSIG RDATA fields | |
513 | // with the Signer's Name field in canonical form and | |
514 | // the Signature field excluded; | |
515 | // zonemd: digest = hash( RR(1) | RR(2) | RR(3) | ... ), so skip RRSIG_RDATA | |
516 | ||
517 | if (includeRRSIG_RDATA) { | |
d06dcda4 | 518 | toHash.append(rrc.serialize(g_rootdnsname, true, true)); |
e7ea7d4e O |
519 | toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end, don't sign the signature! |
520 | } | |
125058a0 PL |
521 | string nameToHash(qname.toDNSStringLC()); |
522 | ||
523 | if (processRRSIGLabels) { | |
524 | unsigned int rrsig_labels = rrc.d_labels; | |
525 | unsigned int fqdn_labels = qname.countLabels(); | |
526 | ||
527 | if (rrsig_labels < fqdn_labels) { | |
528 | DNSName choppedQname(qname); | |
529 | while (choppedQname.countLabels() > rrsig_labels) | |
530 | choppedQname.chopOff(); | |
531 | nameToHash = "\x01*" + choppedQname.toDNSStringLC(); | |
532 | } else if (rrsig_labels > fqdn_labels) { | |
533 | // The RRSIG Labels field is a lie (or the qname is wrong) and the RRSIG | |
534 | // can never be valid | |
535 | return ""; | |
536 | } | |
537 | } | |
538 | ||
d06dcda4 | 539 | for (const shared_ptr<const DNSRecordContent>& add : signRecords) { |
125058a0 | 540 | toHash.append(nameToHash); |
4691b2df BH |
541 | uint16_t tmp=htons(rrc.d_type); |
542 | toHash.append((char*)&tmp, 2); | |
543 | tmp=htons(1); // class | |
544 | toHash.append((char*)&tmp, 2); | |
545 | uint32_t ttl=htonl(rrc.d_originalttl); | |
546 | toHash.append((char*)&ttl, 4); | |
feb53a77 | 547 | // for NSEC signatures, we should not lowercase the rdata section |
12c06211 | 548 | string rdata=add->serialize(g_rootdnsname, true, (add->getType() == QType::NSEC) ? false : true); // RFC 6840, 5.1 |
4691b2df BH |
549 | tmp=htons(rdata.length()); |
550 | toHash.append((char*)&tmp, 2); | |
551 | toHash.append(rdata); | |
552 | } | |
1e05b07c | 553 | |
f309dacd | 554 | return toHash; |
4691b2df BH |
555 | } |
556 | ||
04cee981 OM |
557 | std::unordered_set<unsigned int> DNSCryptoKeyEngine::s_switchedOff; |
558 | ||
559 | bool DNSCryptoKeyEngine::isAlgorithmSwitchedOff(unsigned int algo) | |
560 | { | |
561 | return s_switchedOff.count(algo) != 0; | |
562 | } | |
563 | ||
564 | void DNSCryptoKeyEngine::switchOffAlgorithm(unsigned int algo) | |
565 | { | |
566 | s_switchedOff.insert(algo); | |
567 | } | |
568 | ||
8455425c RG |
569 | bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo) |
570 | { | |
04cee981 OM |
571 | if (isAlgorithmSwitchedOff(algo)) { |
572 | return false; | |
573 | } | |
8455425c | 574 | const makers_t& makers = getMakers(); |
04cee981 | 575 | auto iter = makers.find(algo); |
8455425c RG |
576 | return iter != makers.cend(); |
577 | } | |
578 | ||
579 | static unsigned int digestToAlgorithmNumber(uint8_t digest) | |
580 | { | |
581 | switch(digest) { | |
690b86b7 | 582 | case DNSSECKeeper::DIGEST_SHA1: |
8455425c | 583 | return DNSSECKeeper::RSASHA1; |
690b86b7 | 584 | case DNSSECKeeper::DIGEST_SHA256: |
8455425c | 585 | return DNSSECKeeper::RSASHA256; |
690b86b7 | 586 | case DNSSECKeeper::DIGEST_GOST: |
8455425c | 587 | return DNSSECKeeper::ECCGOST; |
690b86b7 | 588 | case DNSSECKeeper::DIGEST_SHA384: |
8455425c RG |
589 | return DNSSECKeeper::ECDSA384; |
590 | default: | |
591 | throw std::runtime_error("Unknown digest type " + std::to_string(digest)); | |
592 | } | |
593 | return 0; | |
594 | } | |
595 | ||
596 | bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest) | |
597 | { | |
598 | try { | |
599 | unsigned int algo = digestToAlgorithmNumber(digest); | |
600 | return isAlgorithmSupported(algo); | |
601 | } | |
602 | catch(const std::exception& e) { | |
603 | return false; | |
604 | } | |
605 | } | |
606 | ||
607 | DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest) | |
4691b2df BH |
608 | { |
609 | string toHash; | |
1e05b07c | 610 | toHash.assign(qname.toDNSStringLC()); |
d06dcda4 | 611 | toHash.append(drc.serialize(DNSName(), true, true)); |
1e05b07c | 612 | |
4691b2df | 613 | DSRecordContent dsrc; |
8455425c RG |
614 | try { |
615 | unsigned int algo = digestToAlgorithmNumber(digest); | |
a2c6e554 | 616 | auto dpk = DNSCryptoKeyEngine::make(algo); |
39315b97 BH |
617 | dsrc.d_digest = dpk->hash(toHash); |
618 | } | |
8455425c | 619 | catch(const std::exception& e) { |
793bc32f | 620 | throw std::runtime_error("Asked to create (C)DS record of unknown digest type " + std::to_string(digest) + ": " + e.what()); |
8455425c | 621 | } |
1e05b07c | 622 | |
8455425c RG |
623 | dsrc.d_algorithm = drc.d_algorithm; |
624 | dsrc.d_digesttype = digest; | |
625 | dsrc.d_tag = const_cast<DNSKEYRecordContent&>(drc).getTag(); | |
8daea594 | 626 | |
4691b2df BH |
627 | return dsrc; |
628 | } | |
629 | ||
4691b2df | 630 | |
7afd3f74 | 631 | static DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr<DNSCryptoKeyEngine>& pk, uint8_t algorithm, uint16_t flags) |
699e6e37 BH |
632 | { |
633 | DNSKEYRecordContent drc; | |
8daea594 | 634 | |
4691b2df | 635 | drc.d_protocol=3; |
c3c89361 | 636 | drc.d_algorithm = algorithm; |
4691b2df | 637 | |
4c1474f3 | 638 | drc.d_flags=flags; |
699e6e37 | 639 | drc.d_key = pk->getPublicKeyString(); |
8daea594 | 640 | |
4691b2df BH |
641 | return drc; |
642 | } | |
643 | ||
b61e407d | 644 | uint32_t getStartOfWeek() |
4691b2df | 645 | { |
b59b334d | 646 | // coverity[store_truncates_time_t] |
4646277d | 647 | uint32_t now = time(nullptr); |
4691b2df BH |
648 | now -= (now % (7*86400)); |
649 | return now; | |
650 | } | |
651 | ||
28e2e78e | 652 | string hashQNameWithSalt(const NSEC3PARAMRecordContent& ns3prc, const DNSName& qname) |
4691b2df | 653 | { |
e4805005 | 654 | return hashQNameWithSalt(ns3prc.d_salt, ns3prc.d_iterations, qname); |
655 | } | |
656 | ||
657 | string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const DNSName& qname) | |
658 | { | |
2dc20546 | 659 | // rfc5155 section 5 |
e4805005 | 660 | unsigned int times = iterations; |
2880bfe6 RG |
661 | unsigned char hash[SHA_DIGEST_LENGTH]; |
662 | string toHash(qname.toDNSStringLC() + salt); | |
2880bfe6 RG |
663 | |
664 | for (;;) { | |
665 | /* so the first time we hash the (lowercased) qname plus the salt, | |
666 | then the result of the last iteration plus the salt */ | |
667 | SHA1(reinterpret_cast<const unsigned char*>(toHash.c_str()), toHash.length(), hash); | |
668 | if (!times--) { | |
669 | /* we are done, just copy the result and return it */ | |
670 | toHash.assign(reinterpret_cast<char*>(hash), sizeof(hash)); | |
28e2e78e | 671 | break; |
2880bfe6 RG |
672 | } |
673 | if (times == (iterations-1)) { | |
674 | /* first time, we need to replace the qname + salt with | |
675 | the hash plus salt, since the qname will not likely | |
676 | match the size of the hash */ | |
a2e73018 RG |
677 | if (toHash.capacity() < (sizeof(hash) + salt.size())) { |
678 | toHash.reserve(sizeof(hash) + salt.size()); | |
679 | } | |
2880bfe6 RG |
680 | toHash.assign(reinterpret_cast<char*>(hash), sizeof(hash)); |
681 | toHash.append(salt); | |
682 | } | |
683 | else { | |
684 | /* starting with the second iteration, the hash size does not change, so we don't need to copy the salt again */ | |
685 | std::copy(reinterpret_cast<char*>(hash), reinterpret_cast<char*>(hash) + sizeof(hash), toHash.begin()); | |
686 | } | |
4691b2df | 687 | } |
2880bfe6 | 688 | |
28e2e78e | 689 | return toHash; |
4691b2df | 690 | } |
28e2e78e | 691 | |
95823c07 RG |
692 | void incrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-) |
693 | { | |
694 | if(raw.empty()) | |
695 | return; | |
696 | ||
697 | for(string::size_type pos=raw.size(); pos; ) { | |
698 | --pos; | |
699 | unsigned char c = (unsigned char)raw[pos]; | |
700 | ++c; | |
701 | raw[pos] = (char) c; | |
702 | if(c) | |
703 | break; | |
704 | } | |
705 | } | |
706 | ||
707 | void decrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-) | |
708 | { | |
709 | if(raw.empty()) | |
710 | return; | |
711 | ||
712 | for(string::size_type pos=raw.size(); pos; ) { | |
713 | --pos; | |
714 | unsigned char c = (unsigned char)raw[pos]; | |
715 | --c; | |
716 | raw[pos] = (char) c; | |
717 | if(c != 0xff) | |
718 | break; | |
719 | } | |
720 | } | |
721 | ||
a3260095 | 722 | const DNSKEYRecordContent& DNSSECPrivateKey::getDNSKEY() const |
673208a2 | 723 | { |
a3260095 RG |
724 | return d_dnskey; |
725 | } | |
726 | ||
727 | void DNSSECPrivateKey::computeDNSKEY() | |
728 | { | |
729 | d_dnskey = makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm, d_flags); | |
673208a2 | 730 | } |
ed3f8559 | 731 | |
ea3816cf | 732 | static string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hasher) { |
78bcb858 | 733 | |
5e8e902d CH |
734 | const EVP_MD* md_type; |
735 | unsigned int outlen; | |
736 | unsigned char hash[EVP_MAX_MD_SIZE]; | |
737 | switch(hasher) { | |
738 | case TSIG_MD5: | |
739 | md_type = EVP_md5(); | |
740 | break; | |
741 | case TSIG_SHA1: | |
742 | md_type = EVP_sha1(); | |
743 | break; | |
744 | case TSIG_SHA224: | |
745 | md_type = EVP_sha224(); | |
746 | break; | |
747 | case TSIG_SHA256: | |
748 | md_type = EVP_sha256(); | |
749 | break; | |
750 | case TSIG_SHA384: | |
751 | md_type = EVP_sha384(); | |
752 | break; | |
753 | case TSIG_SHA512: | |
754 | md_type = EVP_sha512(); | |
755 | break; | |
756 | default: | |
60a1c204 | 757 | throw PDNSException("Unknown hash algorithm requested from calculateHMAC()"); |
5e8e902d CH |
758 | } |
759 | ||
760 | unsigned char* out = HMAC(md_type, reinterpret_cast<const unsigned char*>(key.c_str()), key.size(), reinterpret_cast<const unsigned char*>(text.c_str()), text.size(), hash, &outlen); | |
4646277d | 761 | if (out == nullptr || outlen == 0) { |
60a1c204 | 762 | throw PDNSException("HMAC computation failed"); |
5e8e902d | 763 | } |
5e8e902d | 764 | |
60a1c204 RG |
765 | return string((char*) hash, outlen); |
766 | } | |
767 | ||
ea3816cf | 768 | static string makeTSIGPayload(const string& previous, const char* packetBegin, size_t packetSize, const DNSName& tsigKeyName, const TSIGRecordContent& trc, bool timersonly) |
01cb2fe2 BH |
769 | { |
770 | string message; | |
c48dec72 | 771 | |
01cb2fe2 BH |
772 | if(!previous.empty()) { |
773 | uint16_t len = htons(previous.length()); | |
ea3816cf | 774 | message.append(reinterpret_cast<const char*>(&len), sizeof(len)); |
01cb2fe2 BH |
775 | message.append(previous); |
776 | } | |
ea3816cf RG |
777 | |
778 | message.append(packetBegin, packetSize); | |
01cb2fe2 BH |
779 | |
780 | vector<uint8_t> signVect; | |
290a083d | 781 | DNSPacketWriter dw(signVect, DNSName(), 0); |
57ddc8ba | 782 | auto pos=signVect.size(); |
01cb2fe2 | 783 | if(!timersonly) { |
ea3816cf | 784 | dw.xfrName(tsigKeyName, false); |
f4d26b4f | 785 | dw.xfr16BitInt(QClass::ANY); // class |
01cb2fe2 | 786 | dw.xfr32BitInt(0); // TTL |
68e9d647 | 787 | dw.xfrName(trc.d_algoName.makeLowerCase(), false); |
01cb2fe2 | 788 | } |
1e05b07c FM |
789 | |
790 | uint32_t now = trc.d_time; | |
01cb2fe2 BH |
791 | dw.xfr48BitInt(now); |
792 | dw.xfr16BitInt(trc.d_fudge); // fudge | |
793 | if(!timersonly) { | |
794 | dw.xfr16BitInt(trc.d_eRcode); // extended rcode | |
795 | dw.xfr16BitInt(trc.d_otherData.length()); // length of 'other' data | |
796 | // dw.xfrBlob(trc->d_otherData); | |
797 | } | |
57ddc8ba | 798 | message.append(signVect.begin()+pos, signVect.end()); |
01cb2fe2 BH |
799 | return message; |
800 | } | |
801 | ||
ea3816cf | 802 | static string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigOffset, const DNSName& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset=0) |
01cb2fe2 | 803 | { |
ea3816cf RG |
804 | string message; |
805 | string packet(opacket); | |
981fa489 | 806 | |
ea3816cf RG |
807 | packet.resize(tsigOffset); // remove the TSIG record at the end as per RFC2845 3.4.1 |
808 | packet[(dnsHeaderOffset + sizeof(struct dnsheader))-1]--; // Decrease ARCOUNT because we removed the TSIG RR in the previous line. | |
1e05b07c | 809 | |
ea3816cf RG |
810 | |
811 | // Replace the message ID with the original message ID from the TSIG record. | |
812 | // This is needed for forwarded DNS Update as they get a new ID when forwarding (section 6.1 of RFC2136). The TSIG record stores the original ID and the | |
813 | // signature was created with the original ID, so we replace it here to get the originally signed message. | |
814 | // If the message is not forwarded, we simply override it with the same id. | |
815 | uint16_t origID = htons(trc.d_origID); | |
816 | packet.replace(0, 2, (char*)&origID, 2); | |
817 | ||
818 | return makeTSIGPayload(previous, packet.data(), packet.size(), keyname, trc, timersonly); | |
819 | } | |
820 | ||
821 | void addTSIG(DNSPacketWriter& pw, TSIGRecordContent& trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly) | |
822 | { | |
823 | TSIGHashEnum algo; | |
824 | if (!getTSIGHashEnum(trc.d_algoName, algo)) { | |
86f1af1c | 825 | throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc.d_algoName.toLogString()); |
01cb2fe2 | 826 | } |
ea3816cf RG |
827 | |
828 | string toSign = makeTSIGPayload(tsigprevious, reinterpret_cast<const char*>(pw.getContent().data()), pw.getContent().size(), tsigkeyname, trc, timersonly); | |
01cb2fe2 | 829 | |
7f9ac49b | 830 | if (algo == TSIG_GSS) { |
b08f1315 OM |
831 | if (!gss_add_signature(tsigkeyname, toSign, trc.d_mac)) { |
832 | throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname.toLogString()+string("'")); | |
833 | } | |
7f9ac49b | 834 | } else { |
ea3816cf RG |
835 | trc.d_mac = calculateHMAC(tsigsecret, toSign, algo); |
836 | // trc.d_mac[0]++; // sabotage | |
7f9ac49b | 837 | } |
e693ff5a | 838 | pw.startRecord(tsigkeyname, QType::TSIG, 0, QClass::ANY, DNSResourceRecord::ADDITIONAL, false); |
ea3816cf | 839 | trc.toPacket(pw); |
01cb2fe2 BH |
840 | pw.commit(); |
841 | } | |
8daea594 | 842 | |
ea3816cf RG |
843 | bool validateTSIG(const std::string& packet, size_t sigPos, const TSIGTriplet& tt, const TSIGRecordContent& trc, const std::string& previousMAC, const std::string& theirMAC, bool timersOnly, unsigned int dnsHeaderOffset) |
844 | { | |
845 | uint64_t delta = std::abs((int64_t)trc.d_time - (int64_t)time(nullptr)); | |
846 | if(delta > trc.d_fudge) { | |
847 | throw std::runtime_error("Invalid TSIG time delta " + std::to_string(delta) + " > fudge " + std::to_string(trc.d_fudge)); | |
848 | } | |
849 | ||
850 | TSIGHashEnum algo; | |
851 | if (!getTSIGHashEnum(trc.d_algoName, algo)) { | |
86f1af1c | 852 | throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc.d_algoName.toLogString()); |
ea3816cf RG |
853 | } |
854 | ||
855 | TSIGHashEnum expectedAlgo; | |
856 | if (!getTSIGHashEnum(tt.algo, expectedAlgo)) { | |
86f1af1c | 857 | throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt.algo.toLogString()); |
ea3816cf RG |
858 | } |
859 | ||
860 | if (algo != expectedAlgo) { | |
86f1af1c | 861 | throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' does not match the expected algorithm (" + tt.algo.toLogString() + " / " + trc.d_algoName.toLogString() + ")"); |
ea3816cf RG |
862 | } |
863 | ||
864 | string tsigMsg; | |
865 | tsigMsg = makeTSIGMessageFromTSIGPacket(packet, sigPos, tt.name, trc, previousMAC, timersOnly, dnsHeaderOffset); | |
866 | ||
867 | if (algo == TSIG_GSS) { | |
b08f1315 OM |
868 | GssContext gssctx(tt.name); |
869 | if (!gss_verify_signature(tt.name, tsigMsg, theirMAC)) { | |
870 | throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate"); | |
871 | } | |
ea3816cf RG |
872 | } else { |
873 | string ourMac = calculateHMAC(tt.secret, tsigMsg, algo); | |
874 | ||
875 | if(!constantTimeStringEquals(ourMac, theirMAC)) { | |
86f1af1c | 876 | throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate"); |
ea3816cf RG |
877 | } |
878 | } | |
879 | ||
880 | return true; | |
881 | } |