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.
25 #include "dnsparser.hh"
28 #include "dnswriter.hh"
29 #include "dnsrecords.hh"
35 #include <boost/algorithm/string.hpp>
36 #include "dnssecinfra.hh"
37 #include "dnsseckeeper.hh"
38 #include <openssl/hmac.h>
39 #include <openssl/sha.h>
40 #include <boost/assign/std/vector.hpp> // for 'operator+=()'
41 #include <boost/assign/list_inserter.hpp>
43 #include "namespaces.hh"
45 #include "pkcs11signers.hh"
47 #include "gss_context.hh"
50 using namespace boost::assign
;
52 shared_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent
& drc
, const char* fname
)
55 FILE *fp
=fopen(fname
, "r");
57 throw runtime_error("Unable to read file '"+string(fname
)+"' for generating DNS Private Key");
60 while(stringfgets(fp
, sline
)) {
64 shared_ptr
<DNSCryptoKeyEngine
> dke
= makeFromISCString(drc
, isc
);
65 if(!dke
->checkKey()) {
66 throw runtime_error("Invalid DNS Private Key in file '"+string(fname
));
71 shared_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent
& drc
, const std::string
& content
)
75 string sline
, key
, value
, raw
;
76 std::istringstream
str(content
);
77 map
<string
, string
> stormap
;
79 while(std::getline(str
, sline
)) {
80 tie(key
,value
)=splitField(sline
, ':');
82 if(pdns_iequals(key
,"algorithm")) {
83 algorithm
= pdns_stou(value
);
84 stormap
["algorithm"]=std::to_string(algorithm
);
86 } else if (pdns_iequals(key
,"pin")) {
89 } else if (pdns_iequals(key
,"engine")) {
90 stormap
["engine"]=value
;
93 } else if (pdns_iequals(key
,"slot")) {
94 stormap
["slot"]=value
;
96 } else if (pdns_iequals(key
,"label")) {
97 stormap
["label"]=value
;
100 else if(pdns_iequals(key
, "Private-key-format"))
103 B64Decode(value
, raw
);
104 stormap
[toLower(key
)]=raw
;
106 shared_ptr
<DNSCryptoKeyEngine
> dpk
;
110 if (stormap
.find("slot") == stormap
.end())
111 throw PDNSException("Cannot load PKCS#11 key, no Slot specified");
112 // we need PIN to be at least empty
113 if (stormap
.find("pin") == stormap
.end()) stormap
["pin"] = "";
114 dpk
= PKCS11DNSCryptoKeyEngine::maker(algorithm
);
116 throw PDNSException("Cannot load PKCS#11 key without support for it");
121 dpk
->fromISCMap(drc
, stormap
);
125 std::string
DNSCryptoKeyEngine::convertToISC() const
127 typedef map
<string
, string
> stormap_t
;
128 storvector_t stormap
= this->convertToISCVector();
130 ret
<<"Private-key-format: v1.2\n";
131 for(const stormap_t::value_type
& value
: stormap
) {
132 if(value
.first
!= "Algorithm" && value
.first
!= "PIN" &&
133 value
.first
!= "Slot" && value
.first
!= "Engine" &&
134 value
.first
!= "Label")
135 ret
<<value
.first
<<": "<<Base64Encode(value
.second
)<<"\n";
137 ret
<<value
.first
<<": "<<value
.second
<<"\n";
142 shared_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::make(unsigned int algo
)
144 const makers_t
& makers
= getMakers();
145 makers_t::const_iterator iter
= makers
.find(algo
);
146 if(iter
!= makers
.cend())
147 return (iter
->second
)(algo
);
149 throw runtime_error("Request to create key object for unknown algorithm number "+std::to_string(algo
));
154 * Returns the supported DNSSEC algorithms with the name of the Crypto Backend used
156 * @return A vector with pairs of (algorithm-number (int), backend-name (string))
158 vector
<pair
<uint8_t, string
>> DNSCryptoKeyEngine::listAllAlgosWithBackend()
160 vector
<pair
<uint8_t, string
>> ret
;
161 for (auto const& value
: getMakers()) {
162 shared_ptr
<DNSCryptoKeyEngine
> dcke(value
.second(value
.first
));
163 ret
.push_back(make_pair(value
.first
, dcke
->getName()));
168 void DNSCryptoKeyEngine::report(unsigned int algo
, maker_t
* maker
, bool fallback
)
170 getAllMakers()[algo
].push_back(maker
);
171 if(getMakers().count(algo
) && fallback
) {
174 getMakers()[algo
]=maker
;
177 bool DNSCryptoKeyEngine::testAll()
181 for(const allmakers_t::value_type
& value
: getAllMakers())
183 for(maker_t
* creator
: value
.second
) {
185 for(maker_t
* signer
: value
.second
) {
186 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
188 for(maker_t
* verifier
: value
.second
) {
190 /* pair<unsigned int, unsigned int> res=*/ testMakers(value
.first
, creator
, signer
, verifier
);
192 catch(std::exception
& e
)
194 cerr
<<e
.what()<<endl
;
204 bool DNSCryptoKeyEngine::testOne(int algo
)
208 for(maker_t
* creator
: getAllMakers()[algo
]) {
210 for(maker_t
* signer
: getAllMakers()[algo
]) {
211 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
213 for(maker_t
* verifier
: getAllMakers()[algo
]) {
215 /* pair<unsigned int, unsigned int> res=*/testMakers(algo
, creator
, signer
, verifier
);
217 catch(std::exception
& e
)
219 cerr
<<e
.what()<<endl
;
227 // returns times it took to sign and verify
228 pair
<unsigned int, unsigned int> DNSCryptoKeyEngine::testMakers(unsigned int algo
, maker_t
* creator
, maker_t
* signer
, maker_t
* verifier
)
230 shared_ptr
<DNSCryptoKeyEngine
> dckeCreate(creator(algo
));
231 shared_ptr
<DNSCryptoKeyEngine
> dckeSign(signer(algo
));
232 shared_ptr
<DNSCryptoKeyEngine
> dckeVerify(verifier(algo
));
234 cerr
<<"Testing algorithm "<<algo
<<": '"<<dckeCreate
->getName()<<"' ->'"<<dckeSign
->getName()<<"' -> '"<<dckeVerify
->getName()<<"' ";
238 else if(algo
== 12 || algo
== 13 || algo
== 15) // ECC-GOST or ECDSAP256SHA256 or ED25519
240 else if(algo
== 14) // ECDSAP384SHA384
242 else if(algo
== 16) // ED448
245 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algo
));
247 dckeCreate
->create(bits
);
249 { // FIXME: this block copy/pasted from makeFromISCString
250 DNSKEYRecordContent dkrc
;
252 string sline
, key
, value
, raw
;
253 std::istringstream
str(dckeCreate
->convertToISC());
254 map
<string
, string
> stormap
;
256 while(std::getline(str
, sline
)) {
257 tie(key
,value
)=splitField(sline
, ':');
259 if(pdns_iequals(key
,"algorithm")) {
260 algorithm
= pdns_stou(value
);
261 stormap
["algorithm"]=std::to_string(algorithm
);
263 } else if (pdns_iequals(key
,"pin")) {
264 stormap
["pin"]=value
;
266 } else if (pdns_iequals(key
,"engine")) {
267 stormap
["engine"]=value
;
269 } else if (pdns_iequals(key
,"slot")) {
270 int slot
= std::stoi(value
);
271 stormap
["slot"]=std::to_string(slot
);
273 } else if (pdns_iequals(key
,"label")) {
274 stormap
["label"]=value
;
277 else if(pdns_iequals(key
, "Private-key-format"))
280 B64Decode(value
, raw
);
281 stormap
[toLower(key
)]=raw
;
283 dckeSign
->fromISCMap(dkrc
, stormap
);
284 if(!dckeSign
->checkKey()) {
285 throw runtime_error("Verification of key with creator "+dckeCreate
->getName()+" with signer "+dckeSign
->getName()+" and verifier "+dckeVerify
->getName()+" failed");
289 string
message("Hi! How is life?");
293 for(unsigned int n
= 0; n
< 100; ++n
)
294 signature
= dckeSign
->sign(message
);
295 unsigned int udiffSign
= dt
.udiff()/100, udiffVerify
;
297 dckeVerify
->fromPublicKeyString(dckeSign
->getPublicKeyString());
298 if (dckeVerify
->getPublicKeyString().compare(dckeSign
->getPublicKeyString())) {
299 throw runtime_error("Comparison of public key loaded into verifier produced by signer failed");
302 if(dckeVerify
->verify(message
, signature
)) {
303 udiffVerify
= dt
.udiff();
304 cerr
<<"Signature & verify ok, signature "<<udiffSign
<<"usec, verify "<<udiffVerify
<<"usec"<<endl
;
307 throw runtime_error("Verification of creator "+dckeCreate
->getName()+" with signer "+dckeSign
->getName()+" and verifier "+dckeVerify
->getName()+" failed");
309 return make_pair(udiffSign
, udiffVerify
);
312 shared_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromPublicKeyString(unsigned int algorithm
, const std::string
& content
)
314 shared_ptr
<DNSCryptoKeyEngine
> dpk
=make(algorithm
);
315 dpk
->fromPublicKeyString(content
);
320 shared_ptr
<DNSCryptoKeyEngine
> DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecordContent
& drc
, const std::string
& raw
)
323 for(const makers_t::value_type
& val
: getMakers())
325 shared_ptr
<DNSCryptoKeyEngine
> ret
=nullptr;
327 ret
= val
.second(val
.first
);
328 ret
->fromPEMString(drc
, raw
);
339 static bool sharedDNSSECCompare(const shared_ptr
<DNSRecordContent
>& a
, const shared_ptr
<DNSRecordContent
>& b
)
341 return a
->serialize(g_rootdnsname
, true, true) < b
->serialize(g_rootdnsname
, true, true);
345 * Returns the string that should be hashed to create/verify the RRSIG content
347 * @param qname DNSName of the RRSIG's owner name.
348 * @param rrc The RRSIGRecordContent we take the Type Covered and
349 * original TTL fields from.
350 * @param signRecords A vector of DNSRecordContent shared_ptr's that are covered
351 * by the RRSIG, where we get the RDATA from.
352 * @param processRRSIGLabels A boolean to trigger processing the RRSIG's "Labels"
353 * field. This is usually only needed for validation
354 * purposes, as the authoritative server correctly
355 * sets qname to the wildcard.
357 string
getMessageForRRSET(const DNSName
& qname
, const RRSIGRecordContent
& rrc
, vector
<shared_ptr
<DNSRecordContent
> >& signRecords
, bool processRRSIGLabels
)
359 sort(signRecords
.begin(), signRecords
.end(), sharedDNSSECCompare
);
362 toHash
.append(const_cast<RRSIGRecordContent
&>(rrc
).serialize(g_rootdnsname
, true, true));
363 toHash
.resize(toHash
.size() - rrc
.d_signature
.length()); // chop off the end, don't sign the signature!
365 string
nameToHash(qname
.toDNSStringLC());
367 if (processRRSIGLabels
) {
368 unsigned int rrsig_labels
= rrc
.d_labels
;
369 unsigned int fqdn_labels
= qname
.countLabels();
371 if (rrsig_labels
< fqdn_labels
) {
372 DNSName
choppedQname(qname
);
373 while (choppedQname
.countLabels() > rrsig_labels
)
374 choppedQname
.chopOff();
375 nameToHash
= "\x01*" + choppedQname
.toDNSStringLC();
376 } else if (rrsig_labels
> fqdn_labels
) {
377 // The RRSIG Labels field is a lie (or the qname is wrong) and the RRSIG
378 // can never be valid
383 for(shared_ptr
<DNSRecordContent
>& add
: signRecords
) {
384 toHash
.append(nameToHash
);
385 uint16_t tmp
=htons(rrc
.d_type
);
386 toHash
.append((char*)&tmp
, 2);
387 tmp
=htons(1); // class
388 toHash
.append((char*)&tmp
, 2);
389 uint32_t ttl
=htonl(rrc
.d_originalttl
);
390 toHash
.append((char*)&ttl
, 4);
391 // for NSEC signatures, we should not lowercase the rdata section
392 string rdata
=add
->serialize(g_rootdnsname
, true, (add
->getType() == QType::NSEC
) ? false : true); // RFC 6840, 5.1
393 tmp
=htons(rdata
.length());
394 toHash
.append((char*)&tmp
, 2);
395 toHash
.append(rdata
);
401 bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo
)
403 const makers_t
& makers
= getMakers();
404 makers_t::const_iterator iter
= makers
.find(algo
);
405 return iter
!= makers
.cend();
408 static unsigned int digestToAlgorithmNumber(uint8_t digest
)
411 case DNSSECKeeper::SHA1
:
412 return DNSSECKeeper::RSASHA1
;
413 case DNSSECKeeper::SHA256
:
414 return DNSSECKeeper::RSASHA256
;
415 case DNSSECKeeper::GOST
:
416 return DNSSECKeeper::ECCGOST
;
417 case DNSSECKeeper::SHA384
:
418 return DNSSECKeeper::ECDSA384
;
420 throw std::runtime_error("Unknown digest type " + std::to_string(digest
));
425 bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest
)
428 unsigned int algo
= digestToAlgorithmNumber(digest
);
429 return isAlgorithmSupported(algo
);
431 catch(const std::exception
& e
) {
436 DSRecordContent
makeDSFromDNSKey(const DNSName
& qname
, const DNSKEYRecordContent
& drc
, uint8_t digest
)
439 toHash
.assign(qname
.toDNSStringLC());
440 toHash
.append(const_cast<DNSKEYRecordContent
&>(drc
).serialize(DNSName(), true, true));
442 DSRecordContent dsrc
;
444 unsigned int algo
= digestToAlgorithmNumber(digest
);
445 shared_ptr
<DNSCryptoKeyEngine
> dpk(DNSCryptoKeyEngine::make(algo
));
446 dsrc
.d_digest
= dpk
->hash(toHash
);
448 catch(const std::exception
& e
) {
449 throw std::runtime_error("Asked to a DS of unknown digest type " + std::to_string(digest
)+"\n");
452 dsrc
.d_algorithm
= drc
.d_algorithm
;
453 dsrc
.d_digesttype
= digest
;
454 dsrc
.d_tag
= const_cast<DNSKEYRecordContent
&>(drc
).getTag();
460 static DNSKEYRecordContent
makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr
<DNSCryptoKeyEngine
> pk
, uint8_t algorithm
, uint16_t flags
)
462 DNSKEYRecordContent drc
;
465 drc
.d_algorithm
= algorithm
;
468 drc
.d_key
= pk
->getPublicKeyString();
473 uint32_t getStartOfWeek()
475 uint32_t now
= time(0);
476 now
-= (now
% (7*86400));
480 string
hashQNameWithSalt(const NSEC3PARAMRecordContent
& ns3prc
, const DNSName
& qname
)
482 return hashQNameWithSalt(ns3prc
.d_salt
, ns3prc
.d_iterations
, qname
);
485 string
hashQNameWithSalt(const std::string
& salt
, unsigned int iterations
, const DNSName
& qname
)
487 unsigned int times
= iterations
;
488 unsigned char hash
[20];
489 string
toHash(qname
.toDNSStringLC());
493 SHA1((unsigned char*)toHash
.c_str(), toHash
.length(), hash
);
494 toHash
.assign((char*)hash
, sizeof(hash
));
501 void incrementHash(std::string
& raw
) // I wonder if this is correct, cmouse? ;-)
506 for(string::size_type pos
=raw
.size(); pos
; ) {
508 unsigned char c
= (unsigned char)raw
[pos
];
516 void decrementHash(std::string
& raw
) // I wonder if this is correct, cmouse? ;-)
521 for(string::size_type pos
=raw
.size(); pos
; ) {
523 unsigned char c
= (unsigned char)raw
[pos
];
531 DNSKEYRecordContent
DNSSECPrivateKey::getDNSKEY() const
533 return makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm
, d_flags
);
539 DEREater(const std::string
& str
) : d_str(str
), d_pos(0)
546 if(d_pos
>= d_str
.length()) {
549 return (uint8_t) d_str
[d_pos
++];
554 uint8_t first
= getByte();
561 for(int n
=0; n
< first
; ++n
) {
568 std::string
getBytes(unsigned int len
)
571 for(unsigned int n
=0; n
< len
; ++n
)
572 ret
.append(1, (char)getByte());
576 std::string::size_type
getOffset()
581 const std::string
& d_str
;
582 std::string::size_type d_pos
;
585 static string
calculateHMAC(const std::string
& key
, const std::string
& text
, TSIGHashEnum hasher
) {
587 const EVP_MD
* md_type
;
589 unsigned char hash
[EVP_MAX_MD_SIZE
];
595 md_type
= EVP_sha1();
598 md_type
= EVP_sha224();
601 md_type
= EVP_sha256();
604 md_type
= EVP_sha384();
607 md_type
= EVP_sha512();
610 throw PDNSException("Unknown hash algorithm requested from calculateHMAC()");
613 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
);
614 if (out
== NULL
|| outlen
== 0) {
615 throw PDNSException("HMAC computation failed");
618 return string((char*) hash
, outlen
);
621 static bool constantTimeStringEquals(const std::string
& a
, const std::string
& b
)
623 if (a
.size() != b
.size()) {
626 const size_t size
= a
.size();
627 #if OPENSSL_VERSION_NUMBER >= 0x0090819fL
628 return CRYPTO_memcmp(a
.c_str(), b
.c_str(), size
) == 0;
630 const volatile unsigned char *_a
= (const volatile unsigned char *) a
.c_str();
631 const volatile unsigned char *_b
= (const volatile unsigned char *) b
.c_str();
632 unsigned char res
= 0;
634 for (size_t idx
= 0; idx
< size
; idx
++) {
635 res
|= _a
[idx
] ^ _b
[idx
];
642 static string
makeTSIGPayload(const string
& previous
, const char* packetBegin
, size_t packetSize
, const DNSName
& tsigKeyName
, const TSIGRecordContent
& trc
, bool timersonly
)
646 if(!previous
.empty()) {
647 uint16_t len
= htons(previous
.length());
648 message
.append(reinterpret_cast<const char*>(&len
), sizeof(len
));
649 message
.append(previous
);
652 message
.append(packetBegin
, packetSize
);
654 vector
<uint8_t> signVect
;
655 DNSPacketWriter
dw(signVect
, DNSName(), 0);
656 auto pos
=signVect
.size();
658 dw
.xfrName(tsigKeyName
, false);
659 dw
.xfr16BitInt(QClass::ANY
); // class
660 dw
.xfr32BitInt(0); // TTL
661 dw
.xfrName(trc
.d_algoName
.makeLowerCase(), false);
664 uint32_t now
= trc
.d_time
;
666 dw
.xfr16BitInt(trc
.d_fudge
); // fudge
668 dw
.xfr16BitInt(trc
.d_eRcode
); // extended rcode
669 dw
.xfr16BitInt(trc
.d_otherData
.length()); // length of 'other' data
670 // dw.xfrBlob(trc->d_otherData);
672 message
.append(signVect
.begin()+pos
, signVect
.end());
676 static string
makeTSIGMessageFromTSIGPacket(const string
& opacket
, unsigned int tsigOffset
, const DNSName
& keyname
, const TSIGRecordContent
& trc
, const string
& previous
, bool timersonly
, unsigned int dnsHeaderOffset
=0)
679 string
packet(opacket
);
681 packet
.resize(tsigOffset
); // remove the TSIG record at the end as per RFC2845 3.4.1
682 packet
[(dnsHeaderOffset
+ sizeof(struct dnsheader
))-1]--; // Decrease ARCOUNT because we removed the TSIG RR in the previous line.
685 // Replace the message ID with the original message ID from the TSIG record.
686 // 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
687 // signature was created with the original ID, so we replace it here to get the originally signed message.
688 // If the message is not forwarded, we simply override it with the same id.
689 uint16_t origID
= htons(trc
.d_origID
);
690 packet
.replace(0, 2, (char*)&origID
, 2);
692 return makeTSIGPayload(previous
, packet
.data(), packet
.size(), keyname
, trc
, timersonly
);
695 void addTSIG(DNSPacketWriter
& pw
, TSIGRecordContent
& trc
, const DNSName
& tsigkeyname
, const string
& tsigsecret
, const string
& tsigprevious
, bool timersonly
)
698 if (!getTSIGHashEnum(trc
.d_algoName
, algo
)) {
699 throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc
.d_algoName
.toString());
702 string toSign
= makeTSIGPayload(tsigprevious
, reinterpret_cast<const char*>(pw
.getContent().data()), pw
.getContent().size(), tsigkeyname
, trc
, timersonly
);
704 if (algo
== TSIG_GSS
) {
705 if (!gss_add_signature(tsigkeyname
, toSign
, trc
.d_mac
)) {
706 throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname
.toString()+string("'"));
709 trc
.d_mac
= calculateHMAC(tsigsecret
, toSign
, algo
);
710 // trc.d_mac[0]++; // sabotage
712 pw
.startRecord(tsigkeyname
, QType::TSIG
, 0, QClass::ANY
, DNSResourceRecord::ADDITIONAL
, false);
717 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
)
719 uint64_t delta
= std::abs((int64_t)trc
.d_time
- (int64_t)time(nullptr));
720 if(delta
> trc
.d_fudge
) {
721 throw std::runtime_error("Invalid TSIG time delta " + std::to_string(delta
) + " > fudge " + std::to_string(trc
.d_fudge
));
725 if (!getTSIGHashEnum(trc
.d_algoName
, algo
)) {
726 throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc
.d_algoName
.toString());
729 TSIGHashEnum expectedAlgo
;
730 if (!getTSIGHashEnum(tt
.algo
, expectedAlgo
)) {
731 throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt
.algo
.toString());
734 if (algo
!= expectedAlgo
) {
735 throw std::runtime_error("Signature with TSIG key '"+tt
.name
.toString()+"' does not match the expected algorithm (" + tt
.algo
.toString() + " / " + trc
.d_algoName
.toString() + ")");
739 tsigMsg
= makeTSIGMessageFromTSIGPacket(packet
, sigPos
, tt
.name
, trc
, previousMAC
, timersOnly
, dnsHeaderOffset
);
741 if (algo
== TSIG_GSS
) {
742 GssContext
gssctx(tt
.name
);
743 if (!gss_verify_signature(tt
.name
, tsigMsg
, theirMAC
)) {
744 throw std::runtime_error("Signature with TSIG key '"+tt
.name
.toString()+"' failed to validate");
747 string ourMac
= calculateHMAC(tt
.secret
, tsigMsg
, algo
);
749 if(!constantTimeStringEquals(ourMac
, theirMAC
)) {
750 throw std::runtime_error("Signature with TSIG key '"+tt
.name
.toString()+"' failed to validate");