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.
26 #include "dnsparser.hh"
29 #include "dnswriter.hh"
30 #include "dnsrecords.hh"
36 #include <boost/algorithm/string.hpp>
37 #include "dnssecinfra.hh"
38 #include "dnsseckeeper.hh"
39 #include <openssl/hmac.h>
40 #include <openssl/sha.h>
41 #include <boost/assign/std/vector.hpp> // for 'operator+=()'
42 #include <boost/assign/list_inserter.hpp>
44 #include "namespaces.hh"
46 #include "pkcs11signers.hh"
48 #include "gss_context.hh"
51 using namespace boost::assign
;
53 std::unique_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent
& drc
, const char* fname
)
56 auto fp
= pdns::UniqueFilePtr(fopen(fname
, "r"));
58 throw runtime_error("Unable to read file '"+string(fname
)+"' for generating DNS Private Key");
61 while(stringfgets(fp
.get(), sline
)) {
66 auto dke
= makeFromISCString(drc
, isc
);
67 auto checkKeyErrors
= std::vector
<std::string
>{};
69 if(!dke
->checkKey(checkKeyErrors
)) {
71 if(!checkKeyErrors
.empty()) {
72 reason
= " ("+boost::algorithm::join(checkKeyErrors
, ", ")+")";
74 throw runtime_error("Invalid DNS Private Key in file '"+string(fname
)+"'"+reason
);
79 std::unique_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent
& drc
, const std::string
& content
)
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
}
101 unsigned int algorithm
= 0;
102 string sline
, key
, value
, raw
;
103 std::istringstream
str(content
);
104 map
<string
, string
> stormap
;
106 while (std::getline(str
, sline
)) {
107 std::tie(key
,value
) = splitField(sline
, ':');
111 const auto it
= knownKeys
.find(key
);
112 if (it
!= knownKeys
.cend()) {
113 if (it
->second
== KeyTypes::str
) {
114 stormap
[key
] = value
;
116 else if (it
->second
== KeyTypes::base64
) {
119 B64Decode(value
, raw
);
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());
126 else if (it
->second
== KeyTypes::numeric
) {
128 auto num
= pdns::checked_stoi
<unsigned int>(value
);
129 stormap
[key
] = std::to_string(num
);
130 if (key
== "algorithm") {
134 catch (const std::exception
& e
) {
135 throw std::runtime_error("Error while trying to parse the numeric value of the '" + key
+ "' key from the ISC map: " + e
.what());
142 B64Decode(value
, raw
);
145 catch (const std::exception
& e
) {
146 stormap
[key
] = value
;
151 std::unique_ptr
<DNSCryptoKeyEngine
> dpk
;
153 if (stormap
.count("engine")) {
155 if (stormap
.count("slot") == 0) {
156 throw PDNSException("Cannot load PKCS#11 key, no Slot specified");
158 // we need PIN to be at least empty
159 if (stormap
.count("pin") == 0) {
162 dpk
= PKCS11DNSCryptoKeyEngine::maker(algorithm
);
164 throw PDNSException("Cannot load PKCS#11 key without support for it");
167 dpk
= make(algorithm
);
169 dpk
->fromISCMap(drc
, stormap
);
173 std::unique_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromPEMFile(DNSKEYRecordContent
& drc
, const uint8_t algorithm
, std::FILE& inputFile
, const std::string
& filename
)
175 auto maker
= DNSCryptoKeyEngine::make(algorithm
);
176 maker
->createFromPEMFile(drc
, inputFile
, filename
);
180 std::unique_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecordContent
& drc
, uint8_t algorithm
, const std::string
& contents
)
182 auto maker
= DNSCryptoKeyEngine::make(algorithm
);
183 maker
->createFromPEMString(drc
, contents
);
187 std::string
DNSCryptoKeyEngine::convertToISC() const
189 storvector_t storvector
= this->convertToISCVector();
191 ret
<< "Private-key-format: v1.2\n";
192 for (const storvector_t::value_type
& value
: storvector
) {
194 if(value
.first
!= "Algorithm" && value
.first
!= "PIN" &&
195 value
.first
!= "Slot" && value
.first
!= "Engine" &&
196 value
.first
!= "Label" && value
.first
!= "PubLabel") {
197 ret
<< value
.first
<< ": " << Base64Encode(value
.second
) << "\n";
200 ret
<< value
.first
<< ": " << value
.second
<< "\n";
207 std::unique_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::make(unsigned int algo
)
209 const makers_t
& makers
= getMakers();
211 auto iter
= makers
.find(algo
);
212 if (iter
!= makers
.cend()) {
213 return (iter
->second
)(algo
);
216 throw runtime_error("Request to create key object for unknown algorithm number " + std::to_string(algo
));
220 * Returns the supported DNSSEC algorithms with the name of the Crypto Backend used
222 * @return A vector with pairs of (algorithm-number (int), backend-name (string))
224 vector
<pair
<uint8_t, string
>> DNSCryptoKeyEngine::listAllAlgosWithBackend()
226 vector
<pair
<uint8_t, string
>> ret
;
227 for (auto const& value
: getMakers()) {
228 auto dcke
= value
.second(value
.first
);
229 ret
.emplace_back(value
.first
, dcke
->getName());
234 string
DNSCryptoKeyEngine::listSupportedAlgoNames()
236 set
<unsigned int> algos
;
237 auto pairs
= DNSCryptoKeyEngine::listAllAlgosWithBackend();
238 for (const auto& pair
: pairs
) {
239 algos
.insert(pair
.first
);
243 for (auto algo
: algos
) {
250 ret
.append(DNSSECKeeper::algorithm2name(algo
));
251 if (isAlgorithmSwitchedOff(algo
)) {
252 ret
.append("(disabled)");
259 void DNSCryptoKeyEngine::report(unsigned int algo
, maker_t
* maker
, bool fallback
)
261 getAllMakers()[algo
].push_back(maker
);
262 if (getMakers().count(algo
) != 0 && fallback
) {
265 getMakers()[algo
] = maker
;
268 bool DNSCryptoKeyEngine::testAll()
272 for(const allmakers_t::value_type
& value
: getAllMakers())
274 for(maker_t
* creator
: value
.second
) {
276 for(maker_t
* signer
: value
.second
) {
277 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
279 for(maker_t
* verifier
: value
.second
) {
281 testMakers(value
.first
, creator
, signer
, verifier
);
283 catch(std::exception
& e
)
285 cerr
<<e
.what()<<endl
;
295 bool DNSCryptoKeyEngine::testOne(int algo
)
299 for(maker_t
* creator
: getAllMakers()[algo
]) {
301 for(maker_t
* signer
: getAllMakers()[algo
]) {
302 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
304 for(maker_t
* verifier
: getAllMakers()[algo
]) {
306 testMakers(algo
, creator
, signer
, verifier
);
308 catch(std::exception
& e
)
310 cerr
<<e
.what()<<endl
;
319 static map
<string
, string
> ISCStringtoMap(const string
& argStr
)
321 unsigned int algorithm
= 0;
326 std::istringstream
str(argStr
);
327 map
<string
, string
> stormap
;
329 while(std::getline(str
, sline
)) {
330 std::tie(key
,value
)=splitField(sline
, ':');
332 if(pdns_iequals(key
,"algorithm")) {
333 pdns::checked_stoi_into(algorithm
, value
);
334 stormap
["algorithm"] = std::to_string(algorithm
);
337 if (pdns_iequals(key
,"pin")) {
338 stormap
["pin"] = value
;
341 if (pdns_iequals(key
,"engine")) {
342 stormap
["engine"] = value
;
345 if (pdns_iequals(key
,"slot")) {
346 int slot
= std::stoi(value
);
347 stormap
["slot"]=std::to_string(slot
);
350 if (pdns_iequals(key
,"label")) {
351 stormap
["label"] = value
;
354 if(pdns_iequals(key
, "Private-key-format")) {
358 B64Decode(value
, raw
);
359 stormap
[toLower(key
)] = raw
;
364 bool DNSCryptoKeyEngine::testVerify(unsigned int algo
, maker_t
* verifier
)
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==";
370 const string pubkey7
= "AwEAAc4n7xPG6yJe6YAsg6oQ+7YjbL7wuDLCP4juOSaDsst2Mehc5eYdT7xJT2H9foTIq7ABkkp8Er1Bh6gDzB/0xvArARdH6DS3P5pUP6w5Zoz4Gu79y3pP6IsR3ZyhiQRSnht1ElnIGZzb1zpi7Y4Y8LZ18NYN2qdLasXx/h6hpRjdcF1s7svZKvfJdvCSgDHHD/JFtDGSOn6qt6i5UFSrObxMUMWbxfOsnqr/eXUQcF/aePdqDXO47yDaSH8sFZoglgvEDiOIkky9DV5VKamvVW8anxE5Vv7y4EPpZKXB3CgUW+NvaoasdgYPFmGM4EcnXh2EFFnSPDL6iwDubiL7s2k=";
371 const string sig7
= "B04Oqmh/nF6BybBGsInauTXH6nlW3VhT2PeSzXVaxQ42QsbbXUgIKuzp2/R7diiEBzbbQ3Eg5vtHOKfEQDkArmOR1oU6yIkyrKHsJkpCvclCyaFiJXrwxkH+A2y8vB+loeDMJKJVwjn7fH9zwBI3Mk7SFuOgYXgzBUNhb5DeQ9RzRbxMcpSc8Cgtjn+QpmTNgL6olpBNsStYz9bSLXBk1EGhmZeBYhliw/2Fse75OoRxIuufKiN6sAD5bKQxp73QQUU+yunVuSeHJizNct8b4f9RXFe49wtZWt5rB0oYXG6zUv0Dq7xJHpUq6v1eB2wf2NucftCKwWu18r4TxkVC5A==";
376 case DNSSECKeeper::RSASHA1
:
380 case DNSSECKeeper::RSASHA1NSEC3SHA1
:
385 throw runtime_error("Verification of verifier called for unimplemented case");
390 B64Decode(b64pubkey
, pubkey
);
391 B64Decode(b64sig
, sig
);
392 auto dckeVerify
= verifier(algo
);
393 dckeVerify
->fromPublicKeyString(pubkey
);
395 auto ret
= dckeVerify
->verify(message
, sig
);
399 bool DNSCryptoKeyEngine::verifyOne(unsigned int algo
)
401 const auto& makers
= getAllMakers();
402 auto iter
= makers
.find(algo
);
404 if (iter
== makers
.cend()) {
407 // Algo found, but maker empty? Should not happen
408 if (iter
->second
.empty()) {
411 // Check that all maker->verify return true
412 return std::all_of(iter
->second
.begin(), iter
->second
.end(), [algo
](maker_t
* verifier
) {
414 if (!testVerify(algo
, verifier
)) {
418 catch (std::exception
& e
) {
425 void DNSCryptoKeyEngine::testMakers(unsigned int algo
, maker_t
* creator
, maker_t
* signer
, maker_t
* verifier
)
427 auto dckeCreate
= creator(algo
);
428 auto dckeSign
= signer(algo
);
429 auto dckeVerify
= verifier(algo
);
431 cout
<<"Testing algorithm "<<algo
<<"("<<DNSSECKeeper::algorithm2name(algo
)<<"): '"<<dckeCreate
->getName()<<"' ->'"<<dckeSign
->getName()<<"' -> '"<<dckeVerify
->getName()<<"' ";
435 else if(algo
== DNSSECKeeper::ECCGOST
|| algo
== DNSSECKeeper::ECDSA256
|| algo
== DNSSECKeeper::ED25519
)
437 else if(algo
== DNSSECKeeper::ECDSA384
)
439 else if(algo
== DNSSECKeeper::ED448
)
442 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algo
));
445 for(unsigned int n
= 0; n
< 100; ++n
)
446 dckeCreate
->create(bits
);
447 cout
<<"("<<dckeCreate
->getBits()<<" bits) ";
448 unsigned int udiffCreate
= dt
.udiff() / 100;
451 DNSKEYRecordContent dkrc
;
452 auto stormap
= ISCStringtoMap(dckeCreate
->convertToISC());
454 dckeSign
->fromISCMap(dkrc
, stormap
);
455 if(!dckeSign
->checkKey()) {
456 throw runtime_error("Verification of key with creator "+dckeCreate
->getName()+" with signer "+dckeSign
->getName()+" and verifier "+dckeVerify
->getName()+" failed");
460 string
message("Hi! How is life?");
464 for(unsigned int n
= 0; n
< 100; ++n
)
465 signature
= dckeSign
->sign(message
);
466 unsigned int udiffSign
= dt
.udiff()/100, udiffVerify
;
468 dckeVerify
->fromPublicKeyString(dckeSign
->getPublicKeyString());
469 if (dckeVerify
->getPublicKeyString().compare(dckeSign
->getPublicKeyString())) {
470 throw runtime_error("Comparison of public key loaded into verifier produced by signer failed");
474 for(unsigned int n
= 0; n
< 100; ++n
)
475 verified
= dckeVerify
->verify(message
, signature
);
478 udiffVerify
= dt
.udiff() / 100;
479 cout
<<"Signature & verify ok, create "<<udiffCreate
<<"us, signature "<<udiffSign
<<"us, verify "<<udiffVerify
<<"us"<<endl
;
482 throw runtime_error("Verification of creator "+dckeCreate
->getName()+" with signer "+dckeSign
->getName()+" and verifier "+dckeVerify
->getName()+" failed");
486 std::unique_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromPublicKeyString(unsigned int algorithm
, const std::string
& content
)
488 auto dpk
= make(algorithm
);
489 dpk
->fromPublicKeyString(content
);
494 * Returns the string that should be hashed to create/verify the RRSIG content
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.
506 string
getMessageForRRSET(const DNSName
& qname
, const RRSIGRecordContent
& rrc
, const sortedRecords_t
& signRecords
, bool processRRSIGLabels
, bool includeRRSIG_RDATA
)
510 // dnssec: signature = sign(RRSIG_RDATA | RR(1) | RR(2)... )
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
517 if (includeRRSIG_RDATA
) {
518 toHash
.append(rrc
.serialize(g_rootdnsname
, true, true));
519 toHash
.resize(toHash
.size() - rrc
.d_signature
.length()); // chop off the end, don't sign the signature!
521 string
nameToHash(qname
.toDNSStringLC());
523 if (processRRSIGLabels
) {
524 unsigned int rrsig_labels
= rrc
.d_labels
;
525 unsigned int fqdn_labels
= qname
.countLabels();
527 if (rrsig_labels
< fqdn_labels
) {
528 DNSName
choppedQname(qname
);
529 while (choppedQname
.countLabels() > rrsig_labels
) {
530 choppedQname
.chopOff();
532 nameToHash
= "\x01*" + choppedQname
.toDNSStringLC();
533 } else if (rrsig_labels
> fqdn_labels
) {
534 // The RRSIG Labels field is a lie (or the qname is wrong) and the RRSIG
535 // can never be valid
540 for (const shared_ptr
<const DNSRecordContent
>& add
: signRecords
) {
541 toHash
.append(nameToHash
);
542 uint16_t tmp
=htons(rrc
.d_type
);
543 toHash
.append((char*)&tmp
, 2);
544 tmp
=htons(1); // class
545 toHash
.append((char*)&tmp
, 2);
546 uint32_t ttl
=htonl(rrc
.d_originalttl
);
547 toHash
.append((char*)&ttl
, 4);
548 // for NSEC signatures, we should not lowercase the rdata section
549 string rdata
=add
->serialize(g_rootdnsname
, true, (add
->getType() == QType::NSEC
) ? false : true); // RFC 6840, 5.1
550 tmp
=htons(rdata
.length());
551 toHash
.append((char*)&tmp
, 2);
552 toHash
.append(rdata
);
558 std::unordered_set
<unsigned int> DNSCryptoKeyEngine::s_switchedOff
;
560 bool DNSCryptoKeyEngine::isAlgorithmSwitchedOff(unsigned int algo
)
562 return s_switchedOff
.count(algo
) != 0;
565 void DNSCryptoKeyEngine::switchOffAlgorithm(unsigned int algo
)
567 s_switchedOff
.insert(algo
);
570 bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo
)
572 if (isAlgorithmSwitchedOff(algo
)) {
575 const makers_t
& makers
= getMakers();
576 auto iter
= makers
.find(algo
);
577 return iter
!= makers
.cend();
580 static unsigned int digestToAlgorithmNumber(uint8_t digest
)
583 case DNSSECKeeper::DIGEST_SHA1
:
584 return DNSSECKeeper::RSASHA1
;
585 case DNSSECKeeper::DIGEST_SHA256
:
586 return DNSSECKeeper::RSASHA256
;
587 case DNSSECKeeper::DIGEST_GOST
:
588 return DNSSECKeeper::ECCGOST
;
589 case DNSSECKeeper::DIGEST_SHA384
:
590 return DNSSECKeeper::ECDSA384
;
592 throw std::runtime_error("Unknown digest type " + std::to_string(digest
));
597 bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest
)
600 unsigned int algo
= digestToAlgorithmNumber(digest
);
601 return isAlgorithmSupported(algo
);
603 catch(const std::exception
& e
) {
608 DSRecordContent
makeDSFromDNSKey(const DNSName
& qname
, const DNSKEYRecordContent
& drc
, uint8_t digest
)
611 toHash
.assign(qname
.toDNSStringLC());
612 toHash
.append(drc
.serialize(DNSName(), true, true));
614 DSRecordContent dsrc
;
616 unsigned int algo
= digestToAlgorithmNumber(digest
);
617 auto dpk
= DNSCryptoKeyEngine::make(algo
);
618 dsrc
.d_digest
= dpk
->hash(toHash
);
620 catch(const std::exception
& e
) {
621 throw std::runtime_error("Asked to create (C)DS record of unknown digest type " + std::to_string(digest
) + ": " + e
.what());
624 dsrc
.d_algorithm
= drc
.d_algorithm
;
625 dsrc
.d_digesttype
= digest
;
626 dsrc
.d_tag
= const_cast<DNSKEYRecordContent
&>(drc
).getTag();
632 static DNSKEYRecordContent
makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr
<DNSCryptoKeyEngine
>& pk
, uint8_t algorithm
, uint16_t flags
)
634 DNSKEYRecordContent drc
;
637 drc
.d_algorithm
= algorithm
;
640 drc
.d_key
= pk
->getPublicKeyString();
645 uint32_t getStartOfWeek()
647 // coverity[store_truncates_time_t]
648 uint32_t now
= time(nullptr);
649 now
-= (now
% (7*86400));
653 string
hashQNameWithSalt(const NSEC3PARAMRecordContent
& ns3prc
, const DNSName
& qname
)
655 return hashQNameWithSalt(ns3prc
.d_salt
, ns3prc
.d_iterations
, qname
);
658 string
hashQNameWithSalt(const std::string
& salt
, unsigned int iterations
, const DNSName
& qname
)
661 unsigned int times
= iterations
;
662 unsigned char hash
[SHA_DIGEST_LENGTH
];
663 string
toHash(qname
.toDNSStringLC() + salt
);
666 /* so the first time we hash the (lowercased) qname plus the salt,
667 then the result of the last iteration plus the salt */
668 SHA1(reinterpret_cast<const unsigned char*>(toHash
.c_str()), toHash
.length(), hash
);
670 /* we are done, just copy the result and return it */
671 toHash
.assign(reinterpret_cast<char*>(hash
), sizeof(hash
));
674 if (times
== (iterations
-1)) {
675 /* first time, we need to replace the qname + salt with
676 the hash plus salt, since the qname will not likely
677 match the size of the hash */
678 if (toHash
.capacity() < (sizeof(hash
) + salt
.size())) {
679 toHash
.reserve(sizeof(hash
) + salt
.size());
681 toHash
.assign(reinterpret_cast<char*>(hash
), sizeof(hash
));
685 /* starting with the second iteration, the hash size does not change, so we don't need to copy the salt again */
686 std::copy(reinterpret_cast<char*>(hash
), reinterpret_cast<char*>(hash
) + sizeof(hash
), toHash
.begin());
693 void incrementHash(std::string
& raw
) // I wonder if this is correct, cmouse? ;-)
698 for(string::size_type pos
=raw
.size(); pos
; ) {
700 unsigned char c
= (unsigned char)raw
[pos
];
708 void decrementHash(std::string
& raw
) // I wonder if this is correct, cmouse? ;-)
713 for(string::size_type pos
=raw
.size(); pos
; ) {
715 unsigned char c
= (unsigned char)raw
[pos
];
723 const DNSKEYRecordContent
& DNSSECPrivateKey::getDNSKEY() const
728 void DNSSECPrivateKey::computeDNSKEY()
730 d_dnskey
= makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm
, d_flags
);
733 static string
calculateHMAC(const std::string
& key
, const std::string
& text
, TSIGHashEnum hasher
) {
735 const EVP_MD
* md_type
;
737 unsigned char hash
[EVP_MAX_MD_SIZE
];
743 md_type
= EVP_sha1();
746 md_type
= EVP_sha224();
749 md_type
= EVP_sha256();
752 md_type
= EVP_sha384();
755 md_type
= EVP_sha512();
758 throw PDNSException("Unknown hash algorithm requested from calculateHMAC()");
761 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
);
762 if (out
== nullptr || outlen
== 0) {
763 throw PDNSException("HMAC computation failed");
766 return string((char*) hash
, outlen
);
769 static string
makeTSIGPayload(const string
& previous
, const char* packetBegin
, size_t packetSize
, const DNSName
& tsigKeyName
, const TSIGRecordContent
& trc
, bool timersonly
)
773 if(!previous
.empty()) {
774 uint16_t len
= htons(previous
.length());
775 message
.append(reinterpret_cast<const char*>(&len
), sizeof(len
));
776 message
.append(previous
);
779 message
.append(packetBegin
, packetSize
);
781 vector
<uint8_t> signVect
;
782 DNSPacketWriter
dw(signVect
, DNSName(), 0);
783 auto pos
=signVect
.size();
785 dw
.xfrName(tsigKeyName
, false);
786 dw
.xfr16BitInt(QClass::ANY
); // class
787 dw
.xfr32BitInt(0); // TTL
788 dw
.xfrName(trc
.d_algoName
.makeLowerCase(), false);
791 uint32_t now
= trc
.d_time
;
793 dw
.xfr16BitInt(trc
.d_fudge
); // fudge
795 dw
.xfr16BitInt(trc
.d_eRcode
); // extended rcode
796 dw
.xfr16BitInt(trc
.d_otherData
.length()); // length of 'other' data
797 // dw.xfrBlob(trc->d_otherData);
799 message
.append(signVect
.begin()+pos
, signVect
.end());
803 static string
makeTSIGMessageFromTSIGPacket(const string
& opacket
, unsigned int tsigOffset
, const DNSName
& keyname
, const TSIGRecordContent
& trc
, const string
& previous
, bool timersonly
, unsigned int dnsHeaderOffset
=0)
806 string
packet(opacket
);
808 packet
.resize(tsigOffset
); // remove the TSIG record at the end as per RFC2845 3.4.1
809 packet
[(dnsHeaderOffset
+ sizeof(struct dnsheader
))-1]--; // Decrease ARCOUNT because we removed the TSIG RR in the previous line.
812 // Replace the message ID with the original message ID from the TSIG record.
813 // 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
814 // signature was created with the original ID, so we replace it here to get the originally signed message.
815 // If the message is not forwarded, we simply override it with the same id.
816 uint16_t origID
= htons(trc
.d_origID
);
817 packet
.replace(0, 2, (char*)&origID
, 2);
819 return makeTSIGPayload(previous
, packet
.data(), packet
.size(), keyname
, trc
, timersonly
);
822 void addTSIG(DNSPacketWriter
& pw
, TSIGRecordContent
& trc
, const DNSName
& tsigkeyname
, const string
& tsigsecret
, const string
& tsigprevious
, bool timersonly
)
825 if (!getTSIGHashEnum(trc
.d_algoName
, algo
)) {
826 throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc
.d_algoName
.toLogString());
829 string toSign
= makeTSIGPayload(tsigprevious
, reinterpret_cast<const char*>(pw
.getContent().data()), pw
.getContent().size(), tsigkeyname
, trc
, timersonly
);
831 if (algo
== TSIG_GSS
) {
832 if (!gss_add_signature(tsigkeyname
, toSign
, trc
.d_mac
)) {
833 throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname
.toLogString()+string("'"));
836 trc
.d_mac
= calculateHMAC(tsigsecret
, toSign
, algo
);
837 // trc.d_mac[0]++; // sabotage
839 pw
.startRecord(tsigkeyname
, QType::TSIG
, 0, QClass::ANY
, DNSResourceRecord::ADDITIONAL
, false);
844 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
)
846 uint64_t delta
= std::abs((int64_t)trc
.d_time
- (int64_t)time(nullptr));
847 if(delta
> trc
.d_fudge
) {
848 throw std::runtime_error("Invalid TSIG time delta " + std::to_string(delta
) + " > fudge " + std::to_string(trc
.d_fudge
));
852 if (!getTSIGHashEnum(trc
.d_algoName
, algo
)) {
853 throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc
.d_algoName
.toLogString());
856 TSIGHashEnum expectedAlgo
;
857 if (!getTSIGHashEnum(tt
.algo
, expectedAlgo
)) {
858 throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt
.algo
.toLogString());
861 if (algo
!= expectedAlgo
) {
862 throw std::runtime_error("Signature with TSIG key '"+tt
.name
.toLogString()+"' does not match the expected algorithm (" + tt
.algo
.toLogString() + " / " + trc
.d_algoName
.toLogString() + ")");
866 tsigMsg
= makeTSIGMessageFromTSIGPacket(packet
, sigPos
, tt
.name
, trc
, previousMAC
, timersOnly
, dnsHeaderOffset
);
868 if (algo
== TSIG_GSS
) {
869 GssContext
gssctx(tt
.name
);
870 if (!gss_verify_signature(tt
.name
, tsigMsg
, theirMAC
)) {
871 throw std::runtime_error("Signature with TSIG key '"+tt
.name
.toLogString()+"' failed to validate");
874 string ourMac
= calculateHMAC(tt
.secret
, tsigMsg
, algo
);
876 if(!constantTimeStringEquals(ourMac
, theirMAC
)) {
877 throw std::runtime_error("Signature with TSIG key '"+tt
.name
.toLogString()+"' failed to validate");