diff --git a/dnsparser.cc b/dnsparser.cc
index ebdcf0a..f063d7c 100644
--- a/dnsparser.cc
+++ b/dnsparser.cc
@@ -273,8 +273,12 @@ void MOADNSParser::init(const char *packet, unsigned int len)
       dr.d_content=std::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr, pr, d_header.opcode));
       d_answers.push_back(make_pair(dr, pr.d_pos));
 
-      if(dr.d_type == QType::TSIG && dr.d_class == 0xff) 
+      if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
+        if(dr.d_place != DNSResourceRecord::ADDITIONAL || n != (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount) - 1) {
+          throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
+        }
         d_tsigPos = recordStartPos + sizeof(struct dnsheader);
+      }
     }
 
 #if 0    
diff --git a/dnsparser.hh b/dnsparser.hh
index 8f00c49..ae01a21 100644
--- a/dnsparser.hh
+++ b/dnsparser.hh
@@ -365,7 +365,7 @@ public:
     return pr;
   }
 
-  uint16_t getTSIGPos()
+  uint16_t getTSIGPos() const
   {
     return d_tsigPos;
   }
diff --git a/dnssecinfra.cc b/dnssecinfra.cc
index 31174ac..6a94950 100644
--- a/dnssecinfra.cc
+++ b/dnssecinfra.cc
@@ -595,15 +595,36 @@ string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEn
       md_type = EVP_sha512();
       break;
     default:
-      throw new PDNSException("Unknown hash algorithm requested from calculateHMAC()");
+      throw PDNSException("Unknown hash algorithm requested from calculateHMAC()");
   }
 
   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);
-  if (out != NULL && outlen > 0) {
-    return string((char*) hash, outlen);
+  if (out == NULL || outlen == 0) {
+    throw PDNSException("HMAC computation failed");
   }
 
-  return "";
+  return string((char*) hash, outlen);
+}
+
+bool constantTimeStringEquals(const std::string& a, const std::string& b)
+{
+  if (a.size() != b.size()) {
+    return false;
+  }
+  const size_t size = a.size();
+#if OPENSSL_VERSION_NUMBER >= 0x0090819fL
+  return CRYPTO_memcmp(a.c_str(), b.c_str(), size) == 0;
+#else
+  const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
+  const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
+  unsigned char res = 0;
+
+  for (size_t idx = 0; idx < size; idx++) {
+    res |= _a[idx] ^ _b[idx];
+  }
+
+  return res == 0;
+#endif
 }
 
 string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigOffset, const DNSName& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset)
diff --git a/dnssecinfra.hh b/dnssecinfra.hh
index 2c0f7ac..149cc3a 100644
--- a/dnssecinfra.hh
+++ b/dnssecinfra.hh
@@ -157,6 +157,7 @@ class DNSPacket;
 void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const std::set<DNSName>& authMap, vector<DNSResourceRecord>& rrs);
 
 string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hash);
+bool constantTimeStringEquals(const std::string& a, const std::string& b);
 
 string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigoffset, const DNSName& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset=0);
 void addTSIG(DNSPacketWriter& pw, TSIGRecordContent* trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly);
diff --git a/ixfr.cc b/ixfr.cc
index d2f49da..fbda54b 100644
--- a/ixfr.cc
+++ b/ixfr.cc
@@ -24,7 +24,7 @@
 #include "dns_random.hh"
 #include "dnsrecords.hh"
 #include "dnssecinfra.hh"
-
+#include "tsigverifier.hh"
 
 // Returns pairs of "remove & add" vectors. If you get an empty remove, it means you got an AXFR!
 vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAddress& master, const DNSName& zone, const DNSRecord& oursr, 
@@ -40,10 +40,11 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
   oursr.d_content->toPacket(pw);
 
   pw.commit();
+  TSIGRecordContent trc;
+  TSIGTCPVerifier tsigVerifier(tt, master, trc);
   if(!tt.algo.empty()) {
     TSIGHashEnum the;
     getTSIGHashEnum(tt.algo, the);
-    TSIGRecordContent trc;
     try {
       trc.d_algoName = getTSIGAlgoName(the);
     } catch(PDNSException& pe) {
@@ -77,6 +78,7 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
   shared_ptr<SOARecordContent> masterSOA;
   vector<DNSRecord> records;
   size_t receivedBytes = 0;
+
   for(;;) {
     if(s.read((char*)&len, 2)!=2)
       break;
@@ -96,6 +98,11 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
       throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode));
 
     //    cout<<"Got a response, rcode: "<<mdp.d_header.rcode<<", got "<<mdp.d_answers.size()<<" answers"<<endl;
+
+    if(!tt.algo.empty()) { // TSIG verify message
+      tsigVerifier.check(std::string(reply, len), mdp);
+    }
+
     for(auto& r: mdp.d_answers) {
       if(r.first.d_type == QType::TSIG) 
         continue;
diff --git a/misc.hh b/misc.hh
index 2b56cd9..a4b2f41 100644
--- a/misc.hh
+++ b/misc.hh
@@ -611,3 +611,4 @@ uid_t strToUID(const string &str);
 gid_t strToGID(const string &str);
 
 unsigned int pdns_stou(const std::string& str, size_t * idx = 0, int base = 10);
+
diff --git a/Makefile.am b/Makefile.am
index d034dc9..8a2e517 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -127,6 +127,7 @@ pdns_recursor_SOURCES = \
 	sortlist.cc sortlist.hh \
 	sstuff.hh \
 	syncres.cc syncres.hh \
+	tsigverifier.cc tsigverifier.hh \
 	ueberbackend.hh \
 	unix_utility.cc \
 	utility.hh \
diff --git a/resolver.cc b/resolver.cc
index b9a62f6..6a265c4 100644
--- a/resolver.cc
+++ b/resolver.cc
@@ -367,7 +367,7 @@ AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
                              const TSIGTriplet& tt, 
                              const ComboAddress* laddr,
                              size_t maxReceivedBytes)
-  : d_tt(tt), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes), d_tsigPos(0), d_nonSignedMessages(0)
+  : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
 {
   ComboAddress local;
   if (laddr != NULL) {
@@ -478,76 +478,13 @@ int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records) //
     if (answer.first.d_type == QType::SOA)
       d_soacount++;
  
-  if(!d_tt.name.empty()) { // TSIG verify message
-    // If we have multiple messages, we need to concatenate them together. We also need to make sure we know the location of 
-    // the TSIG record so we can remove it in makeTSIGMessageFromTSIGPacket
-    d_signData.append(d_buf.get(), len);
-    if (mdp.getTSIGPos() == 0)
-      d_tsigPos += len;
-    else 
-      d_tsigPos += mdp.getTSIGPos();
-
-    string theirMac;
-    bool checkTSIG = false;
-    
-    for(const MOADNSParser::answers_t::value_type& answer :  mdp.d_answers) {
-      if (answer.first.d_type == QType::SOA)  // A SOA is either the first or the last record. We need to check TSIG if that's the case.
-        checkTSIG = true;
-      
-      if(answer.first.d_type == QType::TSIG) {
-        shared_ptr<TSIGRecordContent> trc = getRR<TSIGRecordContent>(answer.first);
-        if(trc) {
-          theirMac = trc->d_mac;
-          d_trc.d_time = trc->d_time;
-          checkTSIG = true;
-        }
-      }
-    }
-
-    if( ! checkTSIG && d_nonSignedMessages > 99) { // We're allowed to get 100 digest without a TSIG.
-      throw ResolverException("No TSIG message received in last 100 messages of AXFR transfer.");
-    }
-
-    if (checkTSIG) {
-      if (theirMac.empty())
-        throw ResolverException("No TSIG on AXFR response from "+d_remote.toStringWithPort()+" , should be signed with TSIG key '"+d_tt.name.toString()+"'");
-
-      string message;
-      if (!d_prevMac.empty()) {
-        message = makeTSIGMessageFromTSIGPacket(d_signData, d_tsigPos, d_tt.name, d_trc, d_prevMac, true, d_signData.size()-len);
-      } else {
-        message = makeTSIGMessageFromTSIGPacket(d_signData, d_tsigPos, d_tt.name, d_trc, d_trc.d_mac, false);
-      }
-
-      TSIGHashEnum algo;
-      if (!getTSIGHashEnum(d_trc.d_algoName, algo)) {
-        throw ResolverException("Unsupported TSIG HMAC algorithm " + d_trc.d_algoName.toString());
-      }
-
-      if (algo == TSIG_GSS) {
-        GssContext gssctx(d_tt.name);
-        if (!gss_verify_signature(d_tt.name, message, theirMac)) {
-          throw ResolverException("Signature failed to validate on AXFR response from "+d_remote.toStringWithPort()+" signed with TSIG key '"+d_tt.name.toString()+"'");
-        }
-      } else {
-        string ourMac=calculateHMAC(d_tt.secret, message, algo);
-
-        // ourMac[0]++; // sabotage == for testing :-)
-        if(ourMac != theirMac) {
-          throw ResolverException("Signature failed to validate on AXFR response from "+d_remote.toStringWithPort()+" signed with TSIG key '"+d_tt.name.toString()+"'");
-        }
-      }
-
-      // Reset and store some values for the next chunks. 
-      d_prevMac = theirMac;
-      d_nonSignedMessages = 0;
-      d_signData.clear();
-      d_tsigPos = 0;
-    }
-    else
-      d_nonSignedMessages++;
+  try {
+    d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
   }
-  
+  catch(const std::runtime_error& re) {
+    throw ResolverException(re.what());
+  }
+
   return true;
 }
 
diff --git a/resolver.hh b/resolver.hh
index 91152db..548551a 100644
--- a/resolver.hh
+++ b/resolver.hh
@@ -41,6 +41,7 @@
 #include "namespaces.hh"
 #include "dnsrecords.hh"
 #include "dnssecinfra.hh"
+#include "tsigverifier.hh"
 
 class ResolverException : public PDNSException
 {
@@ -101,14 +102,10 @@ class AXFRRetriever : public boost::noncopyable
     int d_sock;
     int d_soacount;
     ComboAddress d_remote;
-    
-    TSIGTriplet d_tt;
-    string d_prevMac; // RFC2845 4.4
-    string d_signData;
+    TSIGTCPVerifier d_tsigVerifier;
+
     size_t d_receivedBytes;
     size_t d_maxReceivedBytes;
-    uint32_t d_tsigPos;
-    uint d_nonSignedMessages; // RFC2845 4.4
     TSIGRecordContent d_trc;
 };
 
diff --git a/tsigverifier.cc b/tsigverifier.cc
new file mode 100644
index 0000000..3daeef4
--- /dev/null
+++ b/tsigverifier.cc
@@ -0,0 +1,89 @@
+
+#include "tsigverifier.hh"
+#include "dnssecinfra.hh"
+#include "gss_context.hh"
+
+bool TSIGTCPVerifier::check(const string& data, const MOADNSParser& mdp)
+{
+  if(d_tt.name.empty()) { // TSIG verify message
+    return true;
+  }
+
+  string theirMac;
+  bool checkTSIG = false;
+  // If we have multiple messages, we need to concatenate them together. We also need to make sure we know the location of
+  // the TSIG record so we can remove it in makeTSIGMessageFromTSIGPacket
+  d_signData.append(data);
+  if (mdp.getTSIGPos() == 0) {
+    d_tsigPos += data.size();
+  }
+  else {
+    d_tsigPos += mdp.getTSIGPos();
+  }
+
+  for(const auto& answer :  mdp.d_answers) {
+    if (answer.first.d_type == QType::SOA) {
+      // A SOA is either the first or the last record. We need to check TSIG if that's the case.
+      checkTSIG = true;
+    }
+
+    if(answer.first.d_type == QType::TSIG) {
+      shared_ptr<TSIGRecordContent> trc = getRR<TSIGRecordContent>(answer.first);
+      if(trc) {
+        theirMac = trc->d_mac;
+        d_trc.d_time = trc->d_time;
+        d_trc.d_fudge = trc->d_fudge;
+        checkTSIG = true;
+      }
+    }
+  }
+
+  if(!checkTSIG && d_nonSignedMessages > 99) { // We're allowed to get 100 digest without a TSIG.
+    throw std::runtime_error("No TSIG message received in last 100 messages of AXFR transfer.");
+  }
+
+  if (checkTSIG) {
+    if (theirMac.empty()) {
+      throw std::runtime_error("No TSIG on AXFR response from "+d_remote.toStringWithPort()+" , should be signed with TSIG key '"+d_tt.name.toString()+"'");
+    }
+
+    uint64_t delta = std::abs((int64_t)d_trc.d_time - (int64_t)time(nullptr));
+    if(delta > d_trc.d_fudge) {
+      throw std::runtime_error("Invalid TSIG time delta " + std::to_string(delta) + " >  fudge " + std::to_string(d_trc.d_fudge));
+    }
+    string message;
+    if (!d_prevMac.empty()) {
+      message = makeTSIGMessageFromTSIGPacket(d_signData, d_tsigPos, d_tt.name, d_trc, d_prevMac, true, d_signData.size()-data.size());
+    } else {
+      message = makeTSIGMessageFromTSIGPacket(d_signData, d_tsigPos, d_tt.name, d_trc, d_trc.d_mac, false);
+    }
+
+    TSIGHashEnum algo;
+    if (!getTSIGHashEnum(d_trc.d_algoName, algo)) {
+      throw std::runtime_error("Unsupported TSIG HMAC algorithm " + d_trc.d_algoName.toString());
+    }
+
+    if (algo == TSIG_GSS) {
+      GssContext gssctx(d_tt.name);
+      if (!gss_verify_signature(d_tt.name, message, theirMac)) {
+        throw std::runtime_error("Signature failed to validate on AXFR response from "+d_remote.toStringWithPort()+" signed with TSIG key '"+d_tt.name.toString()+"'");
+      }
+    } else {
+      string ourMac=calculateHMAC(d_tt.secret, message, algo);
+
+      if(!constantTimeStringEquals(ourMac, theirMac)) {
+        throw std::runtime_error("Signature failed to validate on AXFR response from "+d_remote.toStringWithPort()+" signed with TSIG key '"+d_tt.name.toString()+"'");
+      }
+    }
+
+    // Reset and store some values for the next chunks.
+    d_prevMac = theirMac;
+    d_nonSignedMessages = 0;
+    d_signData.clear();
+    d_tsigPos = 0;
+  }
+  else
+    d_nonSignedMessages++;
+
+  return true;
+}
diff --git a/tsigverifier.hh b/tsigverifier.hh
new file mode 100644
index 0000000..84b87eb
--- /dev/null
+++ b/tsigverifier.hh
@@ -0,0 +1,22 @@
+
+#pragma once
+
+#include "dnsrecords.hh"
+#include "iputils.hh"
+
+class TSIGTCPVerifier
+{
+public:
+  TSIGTCPVerifier(const TSIGTriplet& tt, const ComboAddress& remote, TSIGRecordContent& trc): d_tt(tt), d_remote(remote), d_trc(trc)
+  {
+  }
+  bool check(const string& data, const MOADNSParser& mdp);
+private:
+  const TSIGTriplet& d_tt;
+  const ComboAddress& d_remote;
+  TSIGRecordContent& d_trc;
+  string d_prevMac; // RFC2845 4.4
+  string d_signData;
+  size_t d_tsigPos{0};
+  uint8_t d_nonSignedMessages{0}; // RFC2845 4.4
+};
