]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Add DNSSEC tests in the SyncRes unit tests suite
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 28 Apr 2017 11:50:13 +0000 (13:50 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 26 Jun 2017 10:24:08 +0000 (12:24 +0200)
14 files changed:
pdns/dnssecinfra.cc
pdns/dnssecinfra.hh
pdns/dnsseckeeper.hh
pdns/dnssecsigner.cc
pdns/packethandler.cc
pdns/pdnsutil.cc
pdns/recursordist/Makefile.am
pdns/recursordist/test-syncres_cc.cc
pdns/secpoll-recursor.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/validate.cc
pdns/validate.hh
pdns/ws-auth.cc

index 474a597d40af72e5c1fc28fa4b88ef000a04e7cf..95cbc3bdaea1626514809025c127b2b6ab3303ad 100644 (file)
@@ -141,9 +141,9 @@ std::string DNSCryptoKeyEngine::convertToISC() const
 
 shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::make(unsigned int algo)
 {
-  makers_t& makers = getMakers();
+  const makers_t& makers = getMakers();
   makers_t::const_iterator iter = makers.find(algo);
-  if(iter != makers.end())
+  if(iter != makers.cend())
     return (iter->second)(algo);
   else {
     throw runtime_error("Request to create key object for unknown algorithm number "+std::to_string(algo));
@@ -320,7 +320,7 @@ shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPublicKeyString(unsig
 shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecordContent& drc, const std::string& raw)
 {
   
-  for(makers_t::value_type& val :  getMakers())
+  for(const makers_t::value_type& val : getMakers())
   {
     shared_ptr<DNSCryptoKeyEngine> ret=nullptr;
     try {
@@ -336,7 +336,7 @@ shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecor
 }
 
 
-bool sharedDNSSECCompare(const shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b)
+static bool sharedDNSSECCompare(const shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b)
 {
   return a->serialize(g_rootdnsname, true, true) < b->serialize(g_rootdnsname, true, true);
 }
@@ -398,35 +398,60 @@ string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, v
   return toHash;
 }
 
-DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, int digest)
+bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo)
+{
+  const makers_t& makers = getMakers();
+  makers_t::const_iterator iter = makers.find(algo);
+  return iter != makers.cend();
+}
+
+static unsigned int digestToAlgorithmNumber(uint8_t digest)
+{
+  switch(digest) {
+  case DNSSECKeeper::SHA1:
+    return DNSSECKeeper::RSASHA1;
+  case DNSSECKeeper::SHA256:
+    return DNSSECKeeper::RSASHA256;
+  case DNSSECKeeper::GOST:
+    return DNSSECKeeper::ECCGOST;
+  case DNSSECKeeper::SHA384:
+    return DNSSECKeeper::ECDSA384;
+  default:
+    throw std::runtime_error("Unknown digest type " + std::to_string(digest));
+  }
+  return 0;
+}
+
+bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest)
+{
+  try {
+    unsigned int algo = digestToAlgorithmNumber(digest);
+    return isAlgorithmSupported(algo);
+  }
+  catch(const std::exception& e) {
+    return false;
+  }
+}
+
+DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest)
 {
   string toHash;
   toHash.assign(qname.toDNSStringLC()); 
   toHash.append(const_cast<DNSKEYRecordContent&>(drc).serialize(DNSName(), true, true));
   
   DSRecordContent dsrc;
-  if(digest==1) {
-    shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(5)); // gives us SHA1
-    dsrc.d_digest = dpk->hash(toHash);
-  }
-  else if(digest == 2) {
-    shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(8)); // gives us SHA256
-    dsrc.d_digest = dpk->hash(toHash);
-  }
-  else if(digest == 3) {
-    shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(12)); // gives us GOST
-    dsrc.d_digest = dpk->hash(toHash);
-  }
-  else if(digest == 4) {
-    shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(14)); // gives us ECDSAP384
+  try {
+    unsigned int algo = digestToAlgorithmNumber(digest);
+    shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algo));
     dsrc.d_digest = dpk->hash(toHash);
   }
-  else 
+  catch(const std::exception& e) {
     throw std::runtime_error("Asked to a DS of unknown digest type " + std::to_string(digest)+"\n");
+  }
   
-  dsrc.d_algorithm= drc.d_algorithm;
-  dsrc.d_digesttype=digest;
-  dsrc.d_tag=const_cast<DNSKEYRecordContent&>(drc).getTag();
+  dsrc.d_algorithm = drc.d_algorithm;
+  dsrc.d_digesttype = digest;
+  dsrc.d_tag = const_cast<DNSKEYRecordContent&>(drc).getTag();
 
   return dsrc;
 }
@@ -445,21 +470,6 @@ DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr<DNSCr
   return drc;
 }
 
-int countLabels(const std::string& signQName)
-{
-  if(!signQName.empty()) {
-    int count=1;
-    for(string::const_iterator pos = signQName.begin(); pos != signQName.end() ; ++pos)
-      if(*pos == '.' && pos+1 != signQName.end())
-        count++;
-
-    if(boost::starts_with(signQName, "*."))
-      count--;
-    return count;
-  }
-  return 0;
-}
-
 uint32_t getStartOfWeek()
 {
   uint32_t now = time(0);
@@ -542,34 +552,6 @@ private:
   std::string::size_type d_pos;
 };
 
-void decodeDERIntegerSequence(const std::string& input, vector<string>& output)
-{
-  output.clear();
-  DEREater de(input);
-  if(de.getByte() != 0x30) 
-    throw runtime_error("Not a DER sequence");
-  
-  unsigned int seqlen=de.getLength(); 
-  unsigned int startseq=de.getOffset();
-  unsigned int len;
-  string ret;
-  try {
-    for(;;) {
-      uint8_t kind = de.getByte();
-      if(kind != 0x02) 
-        throw runtime_error("DER Sequence contained non-INTEGER component: "+std::to_string(static_cast<unsigned int>(kind)) );
-      len = de.getLength();
-      ret = de.getBytes(len);
-      output.push_back(ret);
-    }
-  }
-  catch(DEREater::eof& eof)
-  {
-    if(de.getOffset() - startseq != seqlen)
-      throw runtime_error("DER Sequence ended before end of data");
-  }  
-}
-
 static string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hasher) {
 
   const EVP_MD* md_type;
index 58d35df09ca2f86d6a375f64ccaa0994eb24e495..d094fdb552215e8a489d4b97499b374fcf2edd9a 100644 (file)
@@ -56,6 +56,10 @@ class DNSCryptoKeyEngine
     virtual std::string getPubKeyHash()const =0;
     virtual std::string getPublicKeyString()const =0;
     virtual int getBits() const =0;
+    virtual unsigned int getAlgorithm() const
+    {
+      return d_algorithm;
+    }
  
     virtual void fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap)=0;
     virtual void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw)
@@ -72,6 +76,8 @@ class DNSCryptoKeyEngine
     static shared_ptr<DNSCryptoKeyEngine> makeFromPEMString(DNSKEYRecordContent& drc, const std::string& raw);
     static shared_ptr<DNSCryptoKeyEngine> makeFromPublicKeyString(unsigned int algorithm, const std::string& raw);
     static shared_ptr<DNSCryptoKeyEngine> make(unsigned int algorithm);
+    static bool isAlgorithmSupported(unsigned int algo);
+    static bool isDigestSupported(uint8_t digest);
     
     typedef shared_ptr<DNSCryptoKeyEngine> maker_t(unsigned int algorithm);
     
@@ -110,6 +116,7 @@ struct DNSSECPrivateKey
   void setKey(const shared_ptr<DNSCryptoKeyEngine> key)
   {
     d_key = key;
+    d_algorithm = key->getAlgorithm();
   }
   DNSKEYRecordContent getDNSKEY() const;
 
@@ -137,27 +144,17 @@ struct CanonicalCompare: public std::binary_function<string, string, bool>
   }
 };
 
-bool sharedDNSSECCompare(const std::shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b);
 string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, std::vector<std::shared_ptr<DNSRecordContent> >& signRecords, bool processRRSIGLabels = false);
 
-DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, int digest=1);
+DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest);
 
-
-class RSAContext;
 class DNSSECKeeper; 
-struct DNSSECPrivateKey;
 
-void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign);
 uint32_t getStartOfWeek();
-void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType, uint32_t signTTL, DNSResourceRecord::Place signPlace,
-  vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSResourceRecord>& outsigned, uint32_t origTTL);
-int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName signQName, uint16_t signQType, uint32_t signTTL,
-  vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent> &rrc);
 
 string hashQNameWithSalt(const NSEC3PARAMRecordContent& ns3prc, const DNSName& qname);
 string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const DNSName& qname);
-void decodeDERIntegerSequence(const std::string& input, vector<string>& output);
-class DNSPacket;
+
 void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const std::set<DNSName>& authMap, vector<DNSZoneRecord>& rrs);
 
 void addTSIG(DNSPacketWriter& pw, TSIGRecordContent& trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly);
index b24d55538096bd1b3c663a636afea86efc4728b9..7f111ecbf74f20a808eba16d523a8c38098440c4 100644 (file)
@@ -55,6 +55,13 @@ public:
     ED448=16
   };
 
+  enum dsdigestalgorithm_t : uint8_t {
+    SHA1=1,
+    SHA256=2,
+    GOST=3,
+    SHA384=4
+  };
+
   struct KeyMetaData
   {
     string fname;
index 031b870891278378a8c5cc930dd3a8d0c9e25d29..864d4737427ac5cb65a5c9083d8e1c54a249da7c 100644 (file)
 #include "statbag.hh"
 extern StatBag S;
 
+static pthread_rwlock_t g_signatures_lock = PTHREAD_RWLOCK_INITIALIZER;
+typedef map<pair<string, string>, string> signaturecache_t;
+static signaturecache_t g_signatures;
+static int g_cacheweekno;
+
+AtomicCounter* g_signatureCount;
+
+static void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign)
+{
+  if(!g_signatureCount)
+    g_signatureCount = S.getPointer("signatures");
+
+  DNSKEYRecordContent drc = dpk.getDNSKEY();
+  const std::shared_ptr<DNSCryptoKeyEngine> rc = dpk.getKey();
+  rrc.d_tag = drc.getTag();
+  rrc.d_algorithm = drc.d_algorithm;
+
+  string msg=getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign
+  pair<string, string> lookup(rc->getPubKeyHash(), pdns_md5sum(msg));  // this hash is a memory saving exercise
+
+  bool doCache=1;
+  if(doCache)
+  {
+    ReadLock l(&g_signatures_lock);
+    signaturecache_t::const_iterator iter = g_signatures.find(lookup);
+    if(iter != g_signatures.end()) {
+      rrc.d_signature=iter->second;
+      return;
+    }
+    // else cerr<<"Miss!"<<endl;
+  }
+
+  rrc.d_signature = rc->sign(msg);
+  (*g_signatureCount)++;
+  if(doCache) {
+    /* we add some jitter here so not all your slaves start pruning their caches at the very same millisecond */
+    int weekno = (time(0) - dns_random(3600)) / (86400*7);  // we just spent milliseconds doing a signature, microsecond more won't kill us
+    const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX);
+
+    WriteLock l(&g_signatures_lock);
+    if(g_cacheweekno < weekno || g_signatures.size() >= (uint) maxcachesize) {  // blunt but effective (C) Habbie, mind04
+      L<<Logger::Warning<<"Cleared signature cache."<<endl;
+      g_signatures.clear();
+      g_cacheweekno = weekno;
+    }
+    g_signatures[lookup] = rrc.d_signature;
+  }
+}
+
 /* this is where the RRSIGs begin, keys are retrieved,
    but the actual signing happens in fillOutRRSIG */
-int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName signQName, uint16_t signQType, uint32_t signTTL,
-                     vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs)
+static int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName signQName, uint16_t signQType, uint32_t signTTL,
+                             vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs)
 {
   if(toSign.empty())
     return -1;
@@ -69,9 +118,9 @@ int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName sig
 }
 
 // this is the entrypoint from DNSPacket
-void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType,
-  uint32_t signTTL, DNSResourceRecord::Place signPlace,
-  vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSZoneRecord>& outsigned, uint32_t origTTL)
+static void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType,
+                         uint32_t signTTL, DNSResourceRecord::Place signPlace,
+                         vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSZoneRecord>& outsigned, uint32_t origTTL)
 {
   //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
   if(toSign.empty())
@@ -104,61 +153,12 @@ void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, con
   toSign.clear();
 }
 
-static pthread_rwlock_t g_signatures_lock = PTHREAD_RWLOCK_INITIALIZER;
-typedef map<pair<string, string>, string> signaturecache_t;
-static signaturecache_t g_signatures;
-static int g_cacheweekno;
-
-AtomicCounter* g_signatureCount;
-
 uint64_t signatureCacheSize(const std::string& str)
 {
   ReadLock l(&g_signatures_lock);
   return g_signatures.size();
 }
 
-void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign) 
-{
-  if(!g_signatureCount)
-    g_signatureCount = S.getPointer("signatures");
-    
-  DNSKEYRecordContent drc = dpk.getDNSKEY(); 
-  const std::shared_ptr<DNSCryptoKeyEngine> rc = dpk.getKey();
-  rrc.d_tag = drc.getTag();
-  rrc.d_algorithm = drc.d_algorithm;
-  
-  string msg=getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign
-  pair<string, string> lookup(rc->getPubKeyHash(), pdns_md5sum(msg));  // this hash is a memory saving exercise
-  
-  bool doCache=1;
-  if(doCache)
-  {
-    ReadLock l(&g_signatures_lock);
-    signaturecache_t::const_iterator iter = g_signatures.find(lookup);
-    if(iter != g_signatures.end()) {
-      rrc.d_signature=iter->second;
-      return;
-    }
-    // else cerr<<"Miss!"<<endl;  
-  }
-  
-  rrc.d_signature = rc->sign(msg);
-  (*g_signatureCount)++;
-  if(doCache) {
-    /* we add some jitter here so not all your slaves start pruning their caches at the very same millisecond */
-    int weekno = (time(0) - dns_random(3600)) / (86400*7);  // we just spent milliseconds doing a signature, microsecond more won't kill us
-    const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX);
-
-    WriteLock l(&g_signatures_lock);
-    if(g_cacheweekno < weekno || g_signatures.size() >= (uint) maxcachesize) {  // blunt but effective (C) Habbie, mind04
-      L<<Logger::Warning<<"Cleared signature cache."<<endl;
-      g_signatures.clear();
-      g_cacheweekno = weekno;
-    }
-    g_signatures[lookup] = rrc.d_signature;
-  }
-}
-
 static bool rrsigncomp(const DNSZoneRecord& a, const DNSZoneRecord& b)
 {
   return tie(a.dr.d_place, a.dr.d_type) < tie(b.dr.d_place, b.dr.d_type);
index 2108f60697ca3211cb5d7e89b6e14eec044f0660..01036d29d15360ec2246034d58435588a49cd41a 100644 (file)
@@ -112,7 +112,6 @@ bool PacketHandler::addCDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd)
 
   DNSZoneRecord rr;
   bool haveOne=false;
-  DNSSECPrivateKey dpk;
 
   DNSSECKeeper::keyset_t entryPoints = d_dk.getEntryPoints(p->qdomain);
   for(const auto& value: entryPoints) {
@@ -149,7 +148,6 @@ bool PacketHandler::addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd)
 {
   DNSZoneRecord rr;
   bool haveOne=false;
-  DNSSECPrivateKey dpk;
 
   DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p->qdomain);
   for(const auto& value: keyset) {
@@ -201,13 +199,12 @@ bool PacketHandler::addCDS(DNSPacket *p, DNSPacket *r, const SOAData& sd)
   rr.auth=true;
 
   bool haveOne=false;
-  DNSSECPrivateKey dpk;
 
   DNSSECKeeper::keyset_t keyset = d_dk.getEntryPoints(p->qdomain);
 
   for(auto const &value : keyset) {
     for(auto const &digestAlgo : digestAlgos){
-      rr.dr.d_content=std::make_shared<DSRecordContent>(makeDSFromDNSKey(p->qdomain, value.first.getDNSKEY(), std::stoi(digestAlgo)));
+      rr.dr.d_content=std::make_shared<DSRecordContent>(makeDSFromDNSKey(p->qdomain, value.first.getDNSKEY(), pdns_stou(digestAlgo)));
       r->addRecord(rr);
       haveOne=true;
     }
index ae464f768355e8c7d13644e32a52893786f5ee0a..079125bed4381bfad8733311ddc2e0960117808c 100644 (file)
@@ -1704,16 +1704,16 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false)
       shown=true;
 
       const std::string prefix(exportDS ? "" : "DS = ");
-      cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
-      cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
+      cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
+      cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA256).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
       try {
-        string output=makeDSFromDNSKey(zone, key, 3).getZoneRepresentation();
+        string output=makeDSFromDNSKey(zone, key, DNSSECKeeper::GOST).getZoneRepresentation();
         cout<<prefix<<zone.toString()<<" IN DS "<<output<< " ; ( GOST R 34.11-94 digest )" << endl;
       }
       catch(...)
       {}
       try {
-        string output=makeDSFromDNSKey(zone, key, 4).getZoneRepresentation();
+        string output=makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA384).getZoneRepresentation();
         cout<<prefix<<zone.toString()<<" IN DS "<<output<< " ; ( SHA-384 digest )" << endl;
       }
       catch(...)
@@ -1755,16 +1755,16 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false)
       if (value.second.keyType == DNSSECKeeper::KSK || value.second.keyType == DNSSECKeeper::CSK) {
         const auto &key = value.first.getDNSKEY();
         const std::string prefix(exportDS ? "" : "DS = ");
-        cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
-        cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
+        cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
+        cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA256).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
         try {
-          string output=makeDSFromDNSKey(zone, key, 3).getZoneRepresentation();
+          string output=makeDSFromDNSKey(zone, key, DNSSECKeeper::GOST).getZoneRepresentation();
           cout<<prefix<<zone.toString()<<" IN DS "<<output<< " ; ( GOST R 34.11-94 digest )" << endl;
         }
         catch(...)
         {}
         try {
-          string output=makeDSFromDNSKey(zone, key, 4).getZoneRepresentation();
+          string output=makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA384).getZoneRepresentation();
           cout<<prefix<<zone.toString()<<" IN DS "<<output<< " ; ( SHA-384 digest )" << endl;
         }
         catch(...)
@@ -2815,8 +2815,8 @@ try
     DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
     cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
     if(dpk.d_flags == 257) {
-      cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 1).getZoneRepresentation() << endl;
-      cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 2).getZoneRepresentation() << endl;
+      cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), DNSSECKeeper::SHA1).getZoneRepresentation() << endl;
+      cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), DNSSECKeeper::SHA256).getZoneRepresentation() << endl;
     }
   }
   else if(cmds[0] == "generate-zone-key") {
index 2299dfdfc098da0404a85864a9df50528c38764d..f2c11e9983bfaa1674f8cd52d79ce301f07c1587 100644 (file)
@@ -186,6 +186,7 @@ testrunner_SOURCES = \
        dnsparser.hh dnsparser.cc \
        dnsrecords.cc \
        dnssecinfra.cc \
+       dnsseckeeper.hh \
        dnswriter.cc dnswriter.hh \
        ednscookies.cc ednscookies.hh \
        ednsoptions.cc ednsoptions.hh \
@@ -200,6 +201,7 @@ testrunner_SOURCES = \
        namespaces.hh \
        nsecrecords.cc \
        pdnsexception.hh \
+       opensslsigners.cc opensslsigners.hh \
        protobuf.cc protobuf.hh \
        qtype.cc qtype.hh \
        randomhelper.cc \
@@ -257,6 +259,9 @@ if LIBSODIUM
 pdns_recursor_SOURCES += \
        sodiumsigners.cc
 pdns_recursor_LDADD += $(LIBSODIUM_LIBS)
+testrunner_SOURCES += \
+       sodiumsigners.cc
+testrunner_LDADD += $(LIBSODIUM_LIBS)
 endif
 
 if LIBDECAF
index 58aae9d503bedd515aee9699650b37b22d8530cf..024092c90b1dbf72fda7d2812841cd446c07eab9 100644 (file)
@@ -3,6 +3,8 @@
 #include <boost/test/unit_test.hpp>
 
 #include "arguments.hh"
+#include "dnssecinfra.hh"
+#include "dnsseckeeper.hh"
 #include "lua-recursor4.hh"
 #include "namespaces.hh"
 #include "rec-lua-conf.hh"
@@ -139,8 +141,16 @@ static void init(bool debug=false)
 
   auto luaconfsCopy = g_luaconfs.getCopy();
   luaconfsCopy.dfe.clear();
+  luaconfsCopy.dsAnchors.clear();
+  for (const auto &dsRecord : rootDSs) {
+    auto ds=unique_ptr<DSRecordContent>(dynamic_cast<DSRecordContent*>(DSRecordContent::make(dsRecord)));
+    luaconfsCopy.dsAnchors[g_rootdnsname].insert(*ds);
+  }
+  luaconfsCopy.negAnchors.clear();
   g_luaconfs.setState(luaconfsCopy);
 
+  g_dnssecmode = DNSSECMode::Off;
+
   ::arg().set("version-string", "string reported on version.pdns or version.bind")="PowerDNS Unit Tests";
 }
 
@@ -171,33 +181,42 @@ static void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false,
   res->d_haveEDNS = edns;
 }
 
-static void addRecordToList(std::vector<DNSRecord>& records, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place, uint32_t ttl)
+static std::shared_ptr<DNSRecordContent> getRecordContent(uint16_t type, const std::string& content)
 {
-  DNSRecord rec;
-  rec.d_place = place;
-  rec.d_name = name;
-  rec.d_type = type;
-  rec.d_ttl = ttl;
+  std::shared_ptr<DNSRecordContent> result = nullptr;
 
   if (type == QType::NS) {
-    rec.d_content = std::make_shared<NSRecordContent>(DNSName(content));
+    result = std::make_shared<NSRecordContent>(DNSName(content));
   }
   else if (type == QType::A) {
-    rec.d_content = std::make_shared<ARecordContent>(ComboAddress(content));
+    result = std::make_shared<ARecordContent>(ComboAddress(content));
   }
   else if (type == QType::AAAA) {
-    rec.d_content = std::make_shared<AAAARecordContent>(ComboAddress(content));
+    result = std::make_shared<AAAARecordContent>(ComboAddress(content));
   }
   else if (type == QType::CNAME) {
-    rec.d_content = std::make_shared<CNAMERecordContent>(DNSName(content));
+    result = std::make_shared<CNAMERecordContent>(DNSName(content));
   }
   else if (type == QType::OPT) {
-    rec.d_content = std::make_shared<OPTRecordContent>();
+    result = std::make_shared<OPTRecordContent>();
   }
   else {
-    rec.d_content = DNSRecordContent::mastermake(type, QClass::IN, content);
+    result = DNSRecordContent::mastermake(type, QClass::IN, content);
   }
 
+  return result;
+}
+
+static void addRecordToList(std::vector<DNSRecord>& records, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place, uint32_t ttl)
+{
+  DNSRecord rec;
+  rec.d_place = place;
+  rec.d_name = name;
+  rec.d_type = type;
+  rec.d_ttl = ttl;
+
+  rec.d_content = getRecordContent(type, content);
+
   records.push_back(rec);
 }
 
@@ -218,20 +237,145 @@ static void addRecordToLW(LWResult* res, const std::string& name, uint16_t type,
 
 static bool isRootServer(const ComboAddress& ip)
 {
-  for (size_t idx = 0; idx < rootIps4Count; idx++) {
-    if (ip.toString() == rootIps4[idx]) {
-      return true;
+  if (ip.isIPv4()) {
+    for (size_t idx = 0; idx < rootIps4Count; idx++) {
+      if (ip.toString() == rootIps4[idx]) {
+        return true;
+      }
     }
   }
-
-  for (size_t idx = 0; idx < rootIps6Count; idx++) {
-    if (ip.toString() == rootIps6[idx]) {
-      return true;
+  else {
+    for (size_t idx = 0; idx < rootIps6Count; idx++) {
+      if (ip.toString() == rootIps6[idx]) {
+        return true;
+      }
     }
   }
+
   return false;
 }
 
+static void computeRRSIG(const DNSSECPrivateKey& dpk, const DNSName& signer, const DNSName& signQName, uint16_t signQType, uint32_t signTTL, uint32_t sigValidity, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign)
+{
+  time_t now = time(nullptr);
+  DNSKEYRecordContent drc = dpk.getDNSKEY();
+  const std::shared_ptr<DNSCryptoKeyEngine> rc = dpk.getKey();
+
+  rrc.d_type = signQType;
+  rrc.d_labels = signQName.countLabels() - signQName.isWildcard();
+  rrc.d_originalttl = signTTL;
+  rrc.d_siginception = now - 1;
+  rrc.d_sigexpire = now + sigValidity;
+  rrc.d_signer = signer;
+  rrc.d_tag = 0;
+  rrc.d_tag = drc.getTag();
+  rrc.d_algorithm = drc.d_algorithm;
+
+  std::string msg = getMessageForRRSET(signQName, rrc, toSign);
+
+  rrc.d_signature = rc->sign(msg);
+}
+
+static void addRRSIG(const std::unordered_map<DNSName, DNSSECPrivateKey>& keys, std::vector<DNSRecord>& records, const DNSName& signer, uint32_t sigValidity)
+{
+  if (records.empty()) {
+    return;
+  }
+
+  const auto it = keys.find(signer);
+  if (it == keys.cend()) {
+    throw std::runtime_error("No DNSKEY found for " + signer.toString() + ", unable to compute the requested RRSIG");
+  }
+
+  size_t recordsCount = records.size();
+  const DNSName& name = records[recordsCount-1].d_name;
+  const uint16_t type = records[recordsCount-1].d_type;
+
+  std::vector<std::shared_ptr<DNSRecordContent> > recordcontents;
+  for (const auto record : records) {
+    if (record.d_name == name && record.d_type == type) {
+      recordcontents.push_back(record.d_content);
+    }
+  }
+
+  RRSIGRecordContent rrc;
+  computeRRSIG(it->second, signer, records[recordsCount-1].d_name, records[recordsCount-1].d_type, records[recordsCount-1].d_ttl, sigValidity, rrc, recordcontents);
+
+  DNSRecord rec;
+  rec.d_place = records[recordsCount-1].d_place;
+  rec.d_name = records[recordsCount-1].d_name;
+  rec.d_type = QType::RRSIG;
+  rec.d_ttl = sigValidity;
+
+  rec.d_content = std::make_shared<RRSIGRecordContent>(rrc);
+  records.push_back(rec);
+}
+
+static void addDNSKEY(const std::unordered_map<DNSName, DNSSECPrivateKey>& keys, const DNSName& signer, uint32_t ttl, std::vector<DNSRecord>& records)
+{
+  const auto it = keys.find(signer);
+  if (it == keys.cend()) {
+    throw std::runtime_error("No DNSKEY found for " + signer.toString());
+  }
+
+  DNSRecord rec;
+  rec.d_place = DNSResourceRecord::ANSWER;
+  rec.d_name = signer;
+  rec.d_type = QType::DNSKEY;
+  rec.d_ttl = ttl;
+
+  rec.d_content = std::make_shared<DNSKEYRecordContent>(it->second.getDNSKEY());
+  records.push_back(rec);
+}
+
+static void addDS(const DNSName& domain, uint32_t ttl, std::vector<DNSRecord>& records)
+{
+  auto luaconfsLocal = g_luaconfs.getLocal();
+  const auto it = luaconfsLocal->dsAnchors.find(domain);
+  if (it == luaconfsLocal->dsAnchors.cend()) {
+    return;
+  }
+
+  for (const auto& ds : it->second) {
+    DNSRecord rec;
+    rec.d_name = domain;
+    rec.d_type = QType::DS;
+    rec.d_place = DNSResourceRecord::AUTHORITY;
+    rec.d_ttl = ttl;
+    rec.d_content = std::make_shared<DSRecordContent>(ds);
+
+    records.push_back(rec);
+  }
+}
+
+static void addNSECRecordToLW(const DNSName& domain, const DNSName& next, const std::set<uint16_t>& types,  uint32_t ttl, std::vector<DNSRecord>& records)
+{
+  NSECRecordContent nrc;
+  nrc.d_next = next;
+  nrc.d_set = types;
+
+  DNSRecord rec;
+  rec.d_name = domain;
+  rec.d_ttl = ttl;
+  rec.d_type = QType::NSEC;
+  rec.d_content = std::make_shared<NSECRecordContent>(nrc);
+  rec.d_place = DNSResourceRecord::AUTHORITY;
+
+  records.push_back(rec);
+}
+
+static void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, std::unordered_map<DNSName, DNSSECPrivateKey>& keys, map<DNSName,dsmap_t>& dsAnchors)
+{
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(algo));
+  dcke->create(dcke->getBits());
+  DNSSECPrivateKey dpk;
+  dpk.d_flags = 256;
+  dpk.setKey(dcke);
+  keys[name] = dpk;
+  DSRecordContent ds = makeDSFromDNSKey(name, dpk.getDNSKEY(), digest);
+  dsAnchors[name].insert(ds);
+}
+
 /* Real tests */
 
 BOOST_AUTO_TEST_SUITE(syncres_cc)
@@ -258,7 +402,7 @@ BOOST_AUTO_TEST_CASE(test_root_primed) {
   BOOST_REQUIRE_EQUAL(ret.size(), 1);
   BOOST_CHECK(ret[0].d_type == QType::AAAA);
   BOOST_CHECK_EQUAL(ret[0].d_name, target);
-
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
 }
 
 BOOST_AUTO_TEST_CASE(test_root_primed_ns) {
@@ -280,8 +424,10 @@ BOOST_AUTO_TEST_CASE(test_root_primed_ns) {
       if (domain == target && type == QType::NS) {
 
         setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
         for (char idx = 'a'; idx <= 'm'; idx++) {
-          addRecordToLW(res, g_rootdnsname, QType::NS, std::to_string(idx) + ".root-servers.net.", DNSResourceRecord::ANSWER, 3600);
+          addr[0] = idx;
+          addRecordToLW(res, g_rootdnsname, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
         }
 
         addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
@@ -509,14 +655,14 @@ BOOST_AUTO_TEST_CASE(test_all_nss_down) {
   sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
       }
       else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
@@ -556,14 +702,14 @@ BOOST_AUTO_TEST_CASE(test_all_nss_network_error) {
   sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
       }
       else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
@@ -605,14 +751,14 @@ BOOST_AUTO_TEST_CASE(test_os_limit_errors) {
   sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
       }
       else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
@@ -666,14 +812,14 @@ BOOST_AUTO_TEST_CASE(test_glued_referral) {
       }
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
       }
       else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
@@ -712,7 +858,7 @@ BOOST_AUTO_TEST_CASE(test_glueless_referral) {
   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
 
         if (domain.isPartOf(DNSName("com."))) {
           addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
@@ -730,7 +876,7 @@ BOOST_AUTO_TEST_CASE(test_glueless_referral) {
       }
       else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
         if (domain == target) {
-          setLWResult(res, 0, true, false, true);
+          setLWResult(res, 0, false, false, true);
           addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
           addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
           return 1;
@@ -816,7 +962,7 @@ BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) {
       if (isRootServer(ip)) {
         BOOST_REQUIRE(!srcmask);
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -854,7 +1000,7 @@ BOOST_AUTO_TEST_CASE(test_following_cname) {
   sr->setAsyncCallback([target, cnameTarget](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -904,7 +1050,7 @@ BOOST_AUTO_TEST_CASE(test_included_poisonous_cname) {
 
       if (isRootServer(ip)) {
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
 
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
@@ -956,7 +1102,7 @@ BOOST_AUTO_TEST_CASE(test_cname_loop) {
 
       if (isRootServer(ip)) {
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -995,7 +1141,7 @@ BOOST_AUTO_TEST_CASE(test_cname_depth) {
 
       if (isRootServer(ip)) {
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -1033,7 +1179,7 @@ BOOST_AUTO_TEST_CASE(test_time_limit) {
       queries++;
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         /* Pretend that this query took 2000 ms */
         res->d_usec = 2000;
 
@@ -1078,7 +1224,7 @@ BOOST_AUTO_TEST_CASE(test_referral_depth) {
       queries++;
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
 
         if (domain == DNSName("www.powerdns.com.")) {
           addRecordToLW(res, domain, QType::NS, "ns.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
@@ -1139,7 +1285,7 @@ BOOST_AUTO_TEST_CASE(test_cname_qperq) {
 
       if (isRootServer(ip)) {
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -1181,7 +1327,7 @@ BOOST_AUTO_TEST_CASE(test_throttled_server) {
 
       if (isRootServer(ip)) {
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -1266,7 +1412,7 @@ BOOST_AUTO_TEST_CASE(test_dont_query_server) {
 
       if (isRootServer(ip)) {
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -1500,7 +1646,7 @@ BOOST_AUTO_TEST_CASE(test_skip_negcache_for_variable_response) {
       BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
 
@@ -1547,7 +1693,7 @@ BOOST_AUTO_TEST_CASE(test_ns_speed) {
   sr->setAsyncCallback([target,&nsCounts](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
@@ -1617,7 +1763,7 @@ BOOST_AUTO_TEST_CASE(test_flawed_nsset) {
   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
 
         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
@@ -1661,7 +1807,7 @@ BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset) {
       queriesCount++;
 
       if (isRootServer(ip) && domain == target) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
         return 1;
@@ -1749,7 +1895,7 @@ BOOST_AUTO_TEST_CASE(test_cache_min_max_ttl) {
 
       if (isRootServer(ip)) {
 
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 7200);
         return 1;
@@ -1800,7 +1946,7 @@ BOOST_AUTO_TEST_CASE(test_cache_expired_ttl) {
   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
 
         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
@@ -1847,7 +1993,7 @@ BOOST_AUTO_TEST_CASE(test_delegation_only) {
   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -1879,7 +2025,7 @@ BOOST_AUTO_TEST_CASE(test_unauth_any) {
   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -2051,7 +2197,7 @@ BOOST_AUTO_TEST_CASE(test_nx_nsec_dnssec) {
 BOOST_AUTO_TEST_CASE(test_qclass_none) {
   std::unique_ptr<SyncRes> sr;
   init();
-  initSR(sr, true, true);
+  initSR(sr, true, false);
 
   primeHints();
 
@@ -2075,7 +2221,7 @@ BOOST_AUTO_TEST_CASE(test_qclass_none) {
 BOOST_AUTO_TEST_CASE(test_xfr) {
   std::unique_ptr<SyncRes> sr;
   init();
-  initSR(sr, true, true);
+  initSR(sr, true, false);
 
   primeHints();
 
@@ -2105,7 +2251,7 @@ BOOST_AUTO_TEST_CASE(test_xfr) {
 BOOST_AUTO_TEST_CASE(test_special_names) {
   std::unique_ptr<SyncRes> sr;
   init();
-  initSR(sr, true, true);
+  initSR(sr, true, false);
 
   primeHints();
 
@@ -2240,7 +2386,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv4_rpz) {
   sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, false, true, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -2282,7 +2428,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv6_rpz) {
   sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -2325,7 +2471,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz) {
   sr->setAsyncCallback([target,ns,nsName](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, nsName.toString(), DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, nsName, QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -2368,7 +2514,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz_disabled) {
   sr->setAsyncCallback([target,ns,nsName](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
 
       if (isRootServer(ip)) {
-        setLWResult(res, 0, true, false, true);
+        setLWResult(res, 0, false, false, true);
         addRecordToLW(res, domain, QType::NS, nsName.toString(), DNSResourceRecord::AUTHORITY, 172800);
         addRecordToLW(res, nsName, QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
         return 1;
@@ -3125,6 +3271,773 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_cache_only) {
   BOOST_CHECK_EQUAL(queriesCount, 0);
 }
 
+BOOST_AUTO_TEST_CASE(test_dnssec_rrsig) {
+//  g_dnssecLOG = true;
+  init();
+
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dcke->create(dcke->getBits());
+  // cerr<<dcke->convertToISC()<<endl;
+  DNSSECPrivateKey dpk;
+  dpk.d_flags = 256;
+  dpk.setKey(dcke);
+
+  std::vector<std::shared_ptr<DNSRecordContent> > recordcontents;
+  recordcontents.push_back(getRecordContent(QType::A, "192.0.2.1"));
+
+  DNSName qname("powerdns.com.");
+
+  RRSIGRecordContent rrc;
+  computeRRSIG(dpk, qname, qname, QType::A, 600, 300, rrc, recordcontents);
+
+  skeyset_t keyset;
+  keyset.insert(std::make_shared<DNSKEYRecordContent>(dpk.getDNSKEY()));
+
+  std::vector<std::shared_ptr<RRSIGRecordContent> > sigs;
+  sigs.push_back(std::make_shared<RRSIGRecordContent>(rrc));
+
+  BOOST_CHECK(validateWithKeySet(time(nullptr), qname, recordcontents, sigs, keyset));
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_csk) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  BOOST_CHECK_EQUAL(queriesCount, 2);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_ksk_zsk) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> zskeys;
+  std::unordered_map<DNSName, DNSSECPrivateKey> kskeys;
+
+  /* Generate key material for "." */
+  auto dckeZ = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dckeZ->create(dckeZ->getBits());
+  DNSSECPrivateKey ksk;
+  ksk.d_flags = 257;
+  ksk.setKey(dckeZ);
+  auto dckeK = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dckeK->create(dckeK->getBits());
+  DNSSECPrivateKey zsk;
+  zsk.d_flags = 256;
+  zsk.setKey(dckeK);
+
+  kskeys[target] = ksk;
+  zskeys[target] = zsk;
+
+  /* Set the root DS */
+  DSRecordContent drc = makeDSFromDNSKey(target, ksk.getDNSKEY(), DNSSECKeeper::SHA256);
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,zskeys,kskeys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(zskeys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        addDNSKEY(kskeys, domain, 300, res->d_records);
+        addDNSKEY(zskeys, domain, 300, res->d_records);
+        addRRSIG(kskeys, res->d_records, domain, 300);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  BOOST_CHECK_EQUAL(queriesCount, 2);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_dnskey) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        /* No DNSKEY */
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  BOOST_CHECK_EQUAL(queriesCount, 2);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_doesnt_match_ds) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> dskeys;
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  /* Generate key material for "." */
+  auto dckeDS = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dckeDS->create(dckeDS->getBits());
+  DNSSECPrivateKey dskey;
+  dskey.d_flags = 257;
+  dskey.setKey(dckeDS);
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dcke->create(dcke->getBits());
+  DNSSECPrivateKey dpk;
+  dpk.d_flags = 256;
+  dpk.setKey(dcke);
+
+  dskeys[target] = dskey;
+  keys[target] = dpk;
+
+  /* Set the root DS */
+  DSRecordContent drc = makeDSFromDNSKey(target, dskey.getDNSKEY(), DNSSECKeeper::SHA256);
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  BOOST_CHECK_EQUAL(queriesCount, 2);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_rrsig_signed_with_unknown_dnskey) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+  std::unordered_map<DNSName, DNSSECPrivateKey> rrsigkeys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+  g_luaconfs.setState(luaconfsCopy);
+
+  auto dckeRRSIG = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dckeRRSIG->create(dckeRRSIG->getBits());
+  DNSSECPrivateKey rrsigkey;
+  rrsigkey.d_flags = 257;
+  rrsigkey.setKey(dckeRRSIG);
+  rrsigkeys[target] = rrsigkey;
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys,rrsigkeys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(rrsigkeys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(rrsigkeys, res->d_records, domain, 300);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  BOOST_CHECK_EQUAL(queriesCount, 2);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_rrsig) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        /* No RRSIG */
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 0 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 13);
+  /* no RRSIG so no query for DNSKEYs */
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_algorithm) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  /* Generate key material for "." */
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dcke->create(dcke->getBits());
+  DNSSECPrivateKey dpk;
+  dpk.d_flags = 256;
+  dpk.setKey(dcke);
+  /* Fake algorithm number (private) */
+  dpk.d_algorithm = 253;
+
+  keys[target] = dpk;
+
+  /* Set the root DS */
+  DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::SHA256);
+  /* Fake algorithm number (private) */
+  drc.d_algorithm = 253;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  /* no supported DS so no query for DNSKEYs */
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_digest) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  /* Generate key material for "." */
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dcke->create(dcke->getBits());
+  DNSSECPrivateKey dpk;
+  dpk.d_flags = 256;
+  dpk.setKey(dcke);
+
+  keys[target] = dpk;
+
+  /* Set the root DS */
+  DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::SHA256);
+  /* Fake digest number (reserved) */
+  drc.d_digesttype = 0;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  /* no supported DS so no query for DNSKEYs */
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_nsec) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target("powerdns.com.");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+  generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (type == QType::DS) {
+        setLWResult(res, 0, false, false, true);
+        addDS(domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+        return 1;
+      }
+      else if (type == QType::DNSKEY) {
+        setLWResult(res, 0, true, false, true);
+        addDNSKEY(keys, domain, 300, res->d_records);
+        addRRSIG(keys, res->d_records, domain, 300);
+        return 1;
+      }
+      else if (domain == target) {
+        if (isRootServer(ip)) {
+          setLWResult(res, 0, false, false, true);
+          addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+          addRRSIG(keys, res->d_records, DNSName("."), 300);
+          addDS(DNSName("com."), 300, res->d_records);
+          addRRSIG(keys, res->d_records, DNSName("."), 300);
+          addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+          return 1;
+        }
+        else if (ip == ComboAddress("192.0.2.1:53")) {
+          setLWResult(res, 0, false, false, true);
+          addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
+          addRRSIG(keys, res->d_records, DNSName("com."), 300);
+          addDS(domain, 300, res->d_records);
+          addRRSIG(keys, res->d_records, DNSName("com."), 300);
+          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+          return 1;
+        }
+        else if (ip == ComboAddress("192.0.2.2:53")) {
+          setLWResult(res, 0, true, false, true);
+          addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
+          addRRSIG(keys, res->d_records, domain, 300);
+          addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS, QType::DNSKEY }, 600, res->d_records);
+          addRRSIG(keys, res->d_records, domain, 300);
+          return 1;
+        }
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  BOOST_REQUIRE_EQUAL(ret.size(), 4);
+  BOOST_CHECK_EQUAL(queriesCount, 6);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_nta) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  /* Generate key material for "." */
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dcke->create(dcke->getBits());
+  DNSSECPrivateKey dpk;
+  dpk.d_flags = 256;
+  dpk.setKey(dcke);
+
+  keys[target] = dpk;
+
+  /* Set the root DS */
+  DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::SHA256);
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc);
+
+  /* Add a NTA for "." */
+  luaconfsCopy.negAnchors[g_rootdnsname] = "NTA for Root";
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRRSIG(keys, res->d_records, domain, 300);
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      } else if (domain == target && type == QType::DNSKEY) {
+
+        setLWResult(res, 0, true, false, true);
+
+        /* No DNSKEY */
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14);
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_no_ta) {
+  std::unique_ptr<SyncRes> sr;
+  init();
+  initSR(sr, true, true);
+
+  g_dnssecmode = DNSSECMode::ValidateAll;
+
+  primeHints();
+  const DNSName target(".");
+  std::unordered_map<DNSName, DNSSECPrivateKey> keys;
+
+  /* Remove the root DS */
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+      queriesCount++;
+
+      if (domain == target && type == QType::NS) {
+
+        setLWResult(res, 0, true, false, true);
+        char addr[] = "a.root-servers.net.";
+        for (char idx = 'a'; idx <= 'm'; idx++) {
+          addr[0] = idx;
+          addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+        }
+
+        addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+        return 1;
+      }
+
+      return 0;
+    });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, 0);
+  /* 13 NS + 0 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 13);
+  BOOST_CHECK_EQUAL(queriesCount, 1);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+}
+
 /*
 // cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
 
index eb44b5624da7b9246d528512a9caab79cb4634e1..19ad912144576ce1802de698813213048c8e1574 100644 (file)
@@ -43,8 +43,6 @@ void doSecPoll(time_t* last_secpoll)
   int res=sr.beginResolve(query, QType(QType::TXT), 1, ret);
 
   if (g_dnssecmode != DNSSECMode::Off && res) {
-    /*ResolveContext ctx;
-      state = validateRecords(ctx, ret);*/
     state = sr.getValidationState();
   }
 
index d27de4153843686668ad3e0da19d19cf76702744..8a6c1e5806c690efd3d327c292a4a6c2de81e05d 100644 (file)
@@ -1187,12 +1187,7 @@ bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress&
 
 bool SyncRes::validationEnabled() const
 {
-  return !d_skipValidation && g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
-}
-
-bool SyncRes::validationRequested() const
-{
-  return d_validationRequested;
+  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
 }
 
 uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures, uint32_t signaturesTTL) const
@@ -1267,19 +1262,35 @@ vState SyncRes::getTA(const DNSName& zone, dsmap_t& ds)
   return Indeterminate;
 }
 
+static size_t countSupportedDS(const dsmap_t& dsmap)
+{
+  size_t count = 0;
+
+  for (const auto& ds : dsmap) {
+    if (isSupportedDS(ds)) {
+      count++;
+    }
+  }
+
+  return count;
+}
+
 vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsigned int depth)
 {
   vState result = getTA(zone, ds);
 
   if (result != Indeterminate || taOnly) {
+    if (result == Secure && countSupportedDS(ds) == 0) {
+      ds.clear();
+      result = Insecure;
+    }
+
     return result;
   }
 
   bool oldSkipCNAME = d_skipCNAMECheck;
-  bool oldValidationRequested = d_validationRequested;
   bool oldRequireAuthData = d_requireAuthData;
   d_skipCNAMECheck = true;
-  d_validationRequested = true;
   d_requireAuthData = false;
 
   std::set<GetBestNSAnswer> beenthere;
@@ -1288,7 +1299,6 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi
   vState state = Indeterminate;
   int rcode = doResolve(zone, QType(QType::DS), dsrecords, depth, beenthere, state);
   d_skipCNAMECheck = oldSkipCNAME;
-  d_validationRequested = oldValidationRequested;
   d_requireAuthData = oldRequireAuthData;
 
   if (rcode == RCode::NoError) {
@@ -1296,7 +1306,7 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi
       for (const auto& record : dsrecords) {
         if (record.d_type == QType::DS) {
           const auto dscontent = getRR<DSRecordContent>(record);
-          if (dscontent) {
+          if (dscontent && isSupportedDS(*dscontent)) {
             ds.insert(*dscontent);
           }
         }
@@ -1322,6 +1332,14 @@ vState SyncRes::getValidationStatus(const DNSName& subdomain, unsigned int depth
   dsmap_t ds;
   vState result = getTA(subdomain, ds);
   if (result != Indeterminate) {
+    if (result == NTA) {
+      result = Insecure;
+    }
+    else if (result == Secure && countSupportedDS(ds) == 0) {
+      ds.clear();
+      result = Insecure;
+    }
+
     return result;
   }
 
@@ -1351,6 +1369,9 @@ vState SyncRes::getValidationStatus(const DNSName& subdomain, unsigned int depth
   result = getDSRecords(subdomain, ds, false, depth);
 
   if (result != Secure) {
+    if (result == NTA) {
+      result = Insecure;
+    }
     return result;
   }
 
@@ -1408,6 +1429,9 @@ vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord
     if (!signer.empty() && signer.isPartOf(zone)) {
       vState state = getDSRecords(signer, ds, false, depth);
       if (state != Secure) {
+        if (state == NTA) {
+          state = Insecure;
+        }
         return state;
       }
     }
@@ -2243,8 +2267,6 @@ int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback) {
   try {
     res=sr.beginResolve(g_rootdnsname, QType(QType::NS), 1, ret);
     if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
-/*      ResolveContext ctx;
-        auto state = validateRecords(ctx, ret);*/
       auto state = sr.getValidationState();
       if (state == Bogus)
         throw PDNSException("Got Bogus validation result for .|NS");
index 2819415877b5b8ce6b585192d94867542dfa37fc..64c6bfd2eb192b4d8abf5c8248b69d8585f03e03 100644 (file)
@@ -637,11 +637,6 @@ public:
     return d_queryValidationState;
   }
 
-  void setValidationRequested()
-  {
-    d_validationRequested = true;
-  }
-
   static thread_local ThreadLocalStorage t_sstorage;
 
   static std::atomic<uint64_t> s_queries;
@@ -737,7 +732,6 @@ private:
   boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem);
 
   bool validationEnabled() const;
-  bool validationRequested() const;
   uint32_t computeLowestTTD(const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures, uint32_t signaturesTTL) const;
   void updateValidationState(vState& state, const vState stateUpdate);
   void updateValidationStatusAfterReferral(const DNSName& newauth, vState& state, unsigned int depth);
@@ -774,9 +768,7 @@ private:
   bool d_incomingECSFound{false};
   bool d_requireAuthData{true};
   bool d_skipCNAMECheck{false};
-  bool d_skipValidation{false};
   bool d_updatingRootNS{false};
-  bool d_validationRequested{false};
   bool d_wantsRPZ{true};
   bool d_wasOutOfBand{false};
   bool d_wasVariable{false};
index 1b097e2f8e6da7e2775bd20892964f0ea632208b..f4362f64c27993b792257b5eb5b0b3dda5f72233 100644 (file)
@@ -152,7 +152,7 @@ static bool checkSignatureWithKey(time_t now, const shared_ptr<RRSIGRecordConten
       LOG("signature by key with tag "<<sig->d_tag<<" was " << (result ? "" : "NOT ")<<"valid"<<endl);
     }
     else {
-      LOG("Signature is expired/not yet valid"<<endl);
+      LOG("Signature is "<<((sig->d_siginception >= now) ? "not yet valid" : "expired")<<endl);
     }
   }
   catch(std::exception& e) {
@@ -277,7 +277,7 @@ void validateDNSKeysAgainstDS(time_t now, const DNSName& zone, const dsmap_t& ds
   for(auto const& dsrc : dsmap)
   {
     auto r = getByTag(tkeys, dsrc.d_tag, dsrc.d_algorithm);
-    //cerr<<"looking at DS with tag "<<dsrc.d_tag<<", algo "<<std::to_string(dsrc.d_algorithm)<<", digest "<<std::to_string(dsrc.d_digesttype)<<" for "<<zone<<", got "<<r.size()<<" DNSKEYs for tag"<<endl;
+    // cerr<<"looking at DS with tag "<<dsrc.d_tag<<", algo "<<std::to_string(dsrc.d_algorithm)<<", digest "<<std::to_string(dsrc.d_digesttype)<<" for "<<zone<<", got "<<r.size()<<" DNSKEYs for tag"<<endl;
 
     for(const auto& drc : r)
     {
@@ -519,6 +519,20 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
   return Bogus;
 }
 
+bool isSupportedDS(const DSRecordContent& ds)
+{
+  if (!DNSCryptoKeyEngine::isAlgorithmSupported(ds.d_algorithm)) {
+    LOG("Discarding DS "<<ds.d_tag<<" because we don't support algorithm number "<<std::to_string(ds.d_algorithm)<<endl);
+    return false;
+  }
+
+  if (!DNSCryptoKeyEngine::isDigestSupported(ds.d_digesttype)) {
+    LOG("Discarding DS "<<ds.d_tag<<" because we don't support digest number "<<std::to_string(ds.d_digesttype)<<endl);
+    return false;
+  }
+
+  return true;
+}
 
 
 
index 149dccb95013a76f53b1498adc81b4616e617e43..32d63e5094f6bd214c168727ea5a3598ca32733d 100644 (file)
@@ -71,3 +71,4 @@ bool getTrustAnchor(const map<DNSName,dsmap_t>& anchors, const DNSName& zone, ds
 bool haveNegativeTrustAnchor(const map<DNSName,std::string>& negAnchors, const DNSName& zone, std::string& reason);
 void validateDNSKeysAgainstDS(time_t now, const DNSName& zone, const dsmap_t& dsmap, const skeyset_t& tkeys, vector<shared_ptr<DNSRecordContent> >& toSign, const vector<shared_ptr<RRSIGRecordContent> >& sigs, skeyset_t& validkeys);
 dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype);
+bool isSupportedDS(const DSRecordContent& ds);
index 78ddccd3e9a1608c9c1061db67773fa4c0eb8953..5142fd9b043055ec4d62d5a50e0eccdb9e528031 100644 (file)
@@ -744,7 +744,7 @@ static void apiZoneCryptokeysGET(DNSName zonename, int inquireKeyId, HttpRespons
 
     if (value.second.keyType == DNSSECKeeper::KSK || value.second.keyType == DNSSECKeeper::CSK) {
       Json::array dses;
-      for(const int keyid : { 1, 2, 3, 4 })
+      for(const uint8_t keyid : { DNSSECKeeper::SHA1, DNSSECKeeper::SHA256, DNSSECKeeper::GOST, DNSSECKeeper::SHA384 })
         try {
           dses.push_back(makeDSFromDNSKey(zonename, value.first.getDNSKEY(), keyid).getZoneRepresentation());
         } catch (...) {}