4 * Copyright (c) 2006 VeriSign. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer. 2. Redistributions in
11 * binary form must reproduce the above copyright notice, this list of
12 * conditions and the following disclaimer in the documentation and/or other
13 * materials provided with the distribution. 3. The name of the author may not
14 * be used to endorse or promote products derived from this software without
15 * specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 package se.rfc.unbound;
32 import java.security.NoSuchAlgorithmException;
35 import org.apache.log4j.Logger;
36 import org.xbill.DNS.*;
37 import org.xbill.DNS.utils.base32;
39 import se.rfc.unbound.validator.DnsSecVerifier;
40 import se.rfc.unbound.validator.SignUtils;
41 import se.rfc.unbound.validator.SignUtils.ByteArrayComparator;
43 public class NSEC3ValUtils
46 // FIXME: should probably refactor to handle different NSEC3 parameters more
48 // Given a list of NSEC3 RRs, they should be grouped according to
49 // parameters. The idea is to hash and compare for each group independently,
50 // instead of having to skip NSEC3 RRs with the wrong parameters.
52 // The logger to use in static methods.
53 private static Logger st_log = Logger.getLogger(NSEC3ValUtils.class);
55 private static Name asterisk_label = Name.fromConstantString("*");
58 * This is a class to encapsulate a unique set of NSEC3 parameters:
59 * algorithm, iterations, and salt.
61 private static class NSEC3Parameters
65 public int iterations;
67 public NSEC3Parameters(NSEC3Record r)
69 alg = r.getHashAlgorithm();
71 iterations = r.getIterations();
74 public boolean match(NSEC3Record r, ByteArrayComparator bac)
76 if (r.getHashAlgorithm() != alg) return false;
77 if (r.getIterations() != iterations) return false;
79 if (salt == null && r.getSalt() != null) return false;
81 if (bac == null) bac = new ByteArrayComparator();
82 return bac.compare(r.getSalt(), salt) == 0;
87 * This is just a simple class to enapsulate the response to a closest
90 private static class CEResponse
92 public Name closestEncloser;
93 public NSEC3Record ce_nsec3;
94 public NSEC3Record nc_nsec3;
96 public CEResponse(Name ce, NSEC3Record nsec3)
98 this.closestEncloser = ce;
99 this.ce_nsec3 = nsec3;
103 public static boolean supportsHashAlgorithm(int alg)
105 if (alg == NSEC3Record.SHA1_DIGEST_ID) return true;
109 public static void stripUnknownAlgNSEC3s(List nsec3s)
111 if (nsec3s == null) return;
112 for (ListIterator i = nsec3s.listIterator(); i.hasNext(); )
114 NSEC3Record nsec3 = (NSEC3Record) i.next();
115 if (!supportsHashAlgorithm(nsec3.getHashAlgorithm()))
123 * Given a list of NSEC3Records that are part of a message, determine the
124 * NSEC3 parameters (hash algorithm, iterations, and salt) present. If there
125 * is more than one distinct grouping, return null;
127 * @param nsec3s A list of NSEC3Record object.
128 * @return A set containing a number of objects (NSEC3Parameter objects)
129 * that correspond to each distinct set of parameters, or null if
130 * the nsec3s list was empty.
132 public static NSEC3Parameters nsec3Parameters(List nsec3s)
134 if (nsec3s == null || nsec3s.size() == 0) return null;
136 NSEC3Parameters params = new NSEC3Parameters((NSEC3Record) nsec3s.get(0));
137 ByteArrayComparator bac = new ByteArrayComparator();
139 for (Iterator i = nsec3s.iterator(); i.hasNext();)
141 if (! params.match((NSEC3Record) i.next(), bac))
150 * In a list of NSEC3Record object pulled from a given message, find the
151 * NSEC3 that directly matches a given name, without hashing.
153 * @param n The name in question.
154 * @param nsec3s A list of NSEC3Records from a given message.
155 * @return The matching NSEC3Record, or null if there wasn't one.
157 // private static NSEC3Record findDirectMatchingNSEC3(Name n, List nsec3s)
159 // if (n == null || nsec3s == null) return null;
161 // for (Iterator i = nsec3s.iterator(); i.hasNext();)
163 // NSEC3Record nsec3 = (NSEC3Record) i.next();
164 // if (n.equals(nsec3.getName())) return nsec3;
170 * Given a hash and an a zone name, construct an NSEC3 ownername.
172 * @param hash The hash of an original name.
173 * @param zonename The zone to use in constructing the NSEC3 name.
174 * @return The NSEC3 name.
176 private static Name hashName(byte[] hash, Name zonename)
180 return new Name(base32.toString(hash).toLowerCase(), zonename);
182 catch (TextParseException e)
184 // Note, this should never happen.
190 * Given a set of NSEC3 parameters, hash a name.
192 * @param name The name to hash.
193 * @param params The parameters to hash with.
196 private static byte[] hash(Name name, NSEC3Parameters params)
200 return NSEC3Record.hash(name,
205 catch (NoSuchAlgorithmException e)
207 st_log.debug("Did not recognize hash algorithm: " + params.alg);
213 * Given the name of a closest encloser, return the name *.closest_encloser.
215 * @param closestEncloser The name to start with.
216 * @return The wildcard name.
218 private static Name ceWildcard(Name closestEncloser)
222 Name wc = Name.concatenate(asterisk_label, closestEncloser);
225 catch (NameTooLongException e)
232 * Given a qname and its proven closest encloser, calculate the "next
233 * closest" name. Basically, this is the name that is one label longer than
234 * the closest encloser that is still a subdomain of qname.
236 * @param qname The qname.
237 * @param closestEncloser The closest encloser name.
238 * @return The next closer name.
240 private static Name nextClosest(Name qname, Name closestEncloser)
242 int strip = qname.labels() - closestEncloser.labels() - 1;
243 return (strip > 0) ? new Name(qname, strip) : qname;
247 * Find the NSEC3Record that matches a hash of a name.
249 * @param hash The pre-calculated hash of a name.
250 * @param zonename The name of the zone that the NSEC3s are from.
251 * @param nsec3s A list of NSEC3Records from a given message.
252 * @param params The parameters used for calculating the hash.
253 * @param bac An already allocated ByteArrayComparator, for reuse. This may
256 * @return The matching NSEC3Record, if one is present.
258 private static NSEC3Record findMatchingNSEC3(byte[] hash, Name zonename,
259 List nsec3s, NSEC3Parameters params, ByteArrayComparator bac)
261 Name n = hashName(hash, zonename);
263 for (Iterator i = nsec3s.iterator(); i.hasNext();)
265 NSEC3Record nsec3 = (NSEC3Record) i.next();
266 // Skip nsec3 records that are using different parameters.
267 if (!params.match(nsec3, bac)) continue;
268 if (n.equals(nsec3.getName())) return nsec3;
274 * Given a hash and a candidate NSEC3Record, determine if that NSEC3Record
275 * covers the hash. Covers specifically means that the hash is in between
276 * the owner and next hashes and does not equal either.
278 * @param nsec3 The candidate NSEC3Record.
279 * @param hash The precalculated hash.
280 * @param bac An already allocated comparator. This may be null.
281 * @return True if the NSEC3Record covers the hash.
283 private static boolean nsec3Covers(NSEC3Record nsec3, byte[] hash,
284 ByteArrayComparator bac)
286 byte[] owner = nsec3.getOwner();
287 byte[] next = nsec3.getNext();
289 // This is the "normal case: owner < next and owner < hash < next
290 if (bac.compare(owner, hash) < 0 && bac.compare(hash, next) < 0)
293 // this is the end of zone case: next < owner && hash > owner || hash <
295 if (bac.compare(next, owner) <= 0
296 && (bac.compare(hash, next) < 0 || bac.compare(owner, hash) < 0))
299 // Otherwise, the NSEC3 does not cover the hash.
304 * Given a pre-hashed name, find a covering NSEC3 from among a list of
307 * @param hash The hash to consider.
308 * @param zonename The name of the zone.
309 * @param nsec3s The list of NSEC3s present in a message.
310 * @param params The NSEC3 parameters used to generate the hash -- NSEC3s
311 * that do not use those parameters will be skipped.
313 * @return A covering NSEC3 if one is present, null otherwise.
315 private static NSEC3Record findCoveringNSEC3(byte[] hash, Name zonename,
316 List nsec3s, NSEC3Parameters params, ByteArrayComparator bac)
318 ByteArrayComparator comparator = new ByteArrayComparator();
320 for (Iterator i = nsec3s.iterator(); i.hasNext();)
322 NSEC3Record nsec3 = (NSEC3Record) i.next();
323 if (!params.match(nsec3, bac)) continue;
325 if (nsec3Covers(nsec3, hash, comparator)) return nsec3;
333 * Given a name and a list of NSEC3s, find the candidate closest encloser.
334 * This will be the first ancestor of 'name' (including itself) to have a
337 * @param name The name the start with.
338 * @param zonename The name of the zone that the NSEC3s came from.
339 * @param nsec3s The list of NSEC3s.
340 * @param nsec3params The NSEC3 parameters.
341 * @param bac A pre-allocated comparator. May be null.
343 * @return A CEResponse containing the closest encloser name and the NSEC3
344 * RR that matched it, or null if there wasn't one.
346 private static CEResponse findClosestEncloser(Name name, Name zonename,
347 List nsec3s, NSEC3Parameters params, ByteArrayComparator bac)
353 // This scans from longest name to shortest, so the first match we find is
354 // the only viable candidate.
355 // FIXME: modify so that the NSEC3 matching the zone apex need not be
357 while (n.labels() >= zonename.labels())
359 nsec3 = findMatchingNSEC3(hash(n, params), zonename, nsec3s, params, bac);
360 if (nsec3 != null) return new CEResponse(n, nsec3);
368 * Given a List of nsec3 RRs, find and prove the closest encloser to qname.
370 * @param qname The qname in question.
371 * @param zonename The name of the zone that the NSEC3 RRs come from.
372 * @param nsec3s The list of NSEC3s found the this response (already
374 * @param params The NSEC3 parameters found in the response.
375 * @param bac A pre-allocated comparator. May be null.
376 * @param proveDoesNotExist If true, then if the closest encloser turns out
377 * to be qname, then null is returned.
378 * @return null if the proof isn't completed. Otherwise, return a CEResponse
379 * object which contains the closest encloser name and the NSEC3
382 private static CEResponse proveClosestEncloser(Name qname, Name zonename,
383 List nsec3s, NSEC3Parameters params, ByteArrayComparator bac,
384 boolean proveDoesNotExist)
386 CEResponse candidate = findClosestEncloser(qname,
392 if (candidate == null)
394 st_log.debug("proveClosestEncloser: could not find a "
395 + "candidate for the closest encloser.");
399 if (candidate.closestEncloser.equals(qname))
401 if (proveDoesNotExist)
403 st_log.debug("proveClosestEncloser: proved that qname existed!");
406 // otherwise, we need to nothing else to prove that qname is its own
411 // If the closest encloser is actually a delegation, then the response
412 // should have been a referral. If it is a DNAME, then it should have been
414 if (candidate.ce_nsec3.hasType(Type.NS)
415 && !candidate.ce_nsec3.hasType(Type.SOA))
417 st_log.debug("proveClosestEncloser: closest encloser "
418 + "was a delegation!");
421 if (candidate.ce_nsec3.hasType(Type.DNAME))
423 st_log.debug("proveClosestEncloser: closest encloser was a DNAME!");
427 // Otherwise, we need to show that the next closer name is covered.
428 Name nextClosest = nextClosest(qname, candidate.closestEncloser);
430 byte[] nc_hash = hash(nextClosest, params);
431 candidate.nc_nsec3 = findCoveringNSEC3(nc_hash,
436 if (candidate.nc_nsec3 == null)
438 st_log.debug("Could not find proof that the "
439 + "closest encloser was the closest encloser");
446 private static int maxIterations(int baseAlg, int keysize)
450 case DnsSecVerifier.RSA:
451 if (keysize == 0) return 2500; // the max at 4096
452 if (keysize > 2048) return 2500;
453 if (keysize > 1024) return 500;
454 if (keysize > 0) return 150;
456 case DnsSecVerifier.DSA:
457 if (keysize == 0) return 5000; // the max at 2048;
458 if (keysize > 1024) return 5000;
459 if (keysize > 0) return 1500;
465 private static boolean validIterations(NSEC3Parameters nsec3params,
466 RRset dnskey_rrset, DnsSecVerifier verifier)
468 // for now, we return the maximum iterations based simply on the key
469 // algorithms that may have been used to sign the NSEC3 RRsets.
471 int max_iterations = 0;
472 for (Iterator i = dnskey_rrset.rrs(); i.hasNext();)
474 DNSKEYRecord dnskey = (DNSKEYRecord) i.next();
475 int baseAlg = verifier.baseAlgorithm(dnskey.getAlgorithm());
476 int iters = maxIterations(baseAlg, 0);
477 max_iterations = max_iterations < iters ? iters : max_iterations;
480 if (nsec3params.iterations > max_iterations) return false;
486 * Determine if all of the NSEC3s in a response are legally ignoreable
487 * (i.e., their presence should lead to an INSECURE result). Currently, this
488 * is solely based on iterations.
490 * @param nsec3s The list of NSEC3s. If there is more than one set of NSEC3
491 * parameters present, this test will not be performed.
492 * @param dnskey_rrset The set of validating DNSKEYs.
493 * @param verifier The verifier used to verify the NSEC3 RRsets. This is
494 * solely used to map algorithm aliases.
495 * @return true if all of the NSEC3s can be legally ignored, false if not.
497 public static boolean allNSEC3sIgnoreable(List nsec3s, RRset dnskey_rrset, DnsSecVerifier verifier)
499 NSEC3Parameters params = nsec3Parameters(nsec3s);
500 if (params == null) return false;
502 return !validIterations(params, dnskey_rrset, verifier);
506 * Determine if the set of NSEC3 records provided with a response prove NAME
507 * ERROR. This means that the NSEC3s prove a) the closest encloser exists,
508 * b) the direct child of the closest encloser towards qname doesn't exist,
509 * and c) *.closest encloser does not exist.
511 * @param nsec3s The list of NSEC3s.
512 * @param qname The query name to check against.
513 * @param zonename This is the name of the zone that the NSEC3s belong to.
514 * This may be discovered in any number of ways. A good one is to
515 * use the signerName from the NSEC3 record's RRSIG.
516 * @return SecurityStatus.SECURE of the Name Error is proven by the NSEC3
517 * RRs, BOGUS if not, INSECURE if all of the NSEC3s could be validly
520 public static boolean proveNameError(List nsec3s, Name qname, Name zonename)
522 if (nsec3s == null || nsec3s.size() == 0) return false;
524 NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
525 if (nsec3params == null)
527 st_log.debug("Could not find a single set of " +
528 "NSEC3 parameters (multiple parameters present).");
532 ByteArrayComparator bac = new ByteArrayComparator();
534 // First locate and prove the closest encloser to qname. We will use the
535 // variant that fails if the closest encloser turns out to be qname.
536 CEResponse ce = proveClosestEncloser(qname,
545 st_log.debug("proveNameError: failed to prove a closest encloser.");
549 // At this point, we know that qname does not exist. Now we need to prove
550 // that the wildcard does not exist.
551 Name wc = ceWildcard(ce.closestEncloser);
552 byte[] wc_hash = hash(wc, nsec3params);
553 NSEC3Record nsec3 = findCoveringNSEC3(wc_hash,
560 st_log.debug("proveNameError: could not prove that the "
561 + "applicable wildcard did not exist.");
569 * Determine if the set of NSEC3 records provided with a response prove NAME
570 * ERROR when qtype = NSEC3. This is a special case, and (currently anyway)
571 * it suffices to simply prove that the NSEC3 RRset itself does not exist,
572 * without proving that no wildcard could have generated it, etc..
574 * @param nsec3s The list of NSEC3s.
575 * @param qname The query name to check against.
576 * @param zonename This is the name of the zone that the NSEC3s belong to.
577 * This may be discovered in any number of ways. A good one is to
578 * use the signerName from the NSEC3 record's RRSIG.
579 * @return true of the Name Error is proven by the NSEC3 RRs, false if not.
581 // public static boolean proveNSEC3NameError(List nsec3s, Name qname,
584 // if (nsec3s == null || nsec3s.size() == 0) return false;
586 // for (Iterator i = nsec3s.iterator(); i.hasNext(); )
588 // NSEC3Record nsec3 = (NSEC3Record) i.next();
590 // // Convert owner and next into Names.
591 // Name owner = nsec3.getName();
595 // next = new Name(base32.toString(nsec3.getNext()), zonename);
597 // catch (TextParseException e)
602 // // Now see if qname is covered by the NSEC3.
604 // // normal case, owner < qname < next.
605 // if (owner.compareTo(next) < 0 && owner.compareTo(qname) < 0 &&
606 // next.compareTo(qname) > 0)
608 // st_log.debug("proveNSEC3NameError: found a covering NSEC3: " + nsec3);
611 // // end-of-zone case: next < owner and qname > owner || qname < next.
612 // if (owner.compareTo(next) > 0 && (owner.compareTo(qname) < 0 ||
613 // next.compareTo(qname) > 0))
615 // st_log.debug("proveNSEC3NameError: found a covering NSEC3: " + nsec3);
620 // st_log.debug("proveNSEC3NameError: did not find a covering NSEC3");
624 * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA
625 * status. There are a number of different variants to this:
627 * 1) Normal NODATA -- qname is matched to an NSEC3 record, type is not
630 * 2) ENT NODATA -- because there must be NSEC3 record for
631 * empty-non-terminals, this is the same as #1.
633 * 3) NSEC3 ownername NODATA -- qname matched an existing, lone NSEC3
634 * ownername, but qtype was not NSEC3. NOTE: as of nsec-05, this case no
637 * 4) Wildcard NODATA -- A wildcard matched the name, but not the type.
639 * 5) Opt-In DS NODATA -- the qname is covered by an opt-in span and qtype ==
640 * DS. (or maybe some future record with the same parent-side-only property)
642 * @param nsec3s The NSEC3Records to consider.
643 * @param qname The qname in question.
644 * @param qtype The qtype in question.
645 * @param zonename The name of the zone that the NSEC3s came from.
646 * @return true if the NSEC3s prove the proposition.
648 public static boolean proveNodata(List nsec3s, Name qname, int qtype,
651 if (nsec3s == null || nsec3s.size() == 0) return false;
653 NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
654 if (nsec3params == null)
656 st_log.debug("could not find a single set of "
657 + "NSEC3 parameters (multiple parameters present)");
660 ByteArrayComparator bac = new ByteArrayComparator();
662 NSEC3Record nsec3 = findMatchingNSEC3(hash(qname, nsec3params),
670 if (nsec3.hasType(qtype))
672 st_log.debug("proveNodata: Matching NSEC3 proved that type existed!");
675 if (nsec3.hasType(Type.CNAME))
677 st_log.debug("proveNodata: Matching NSEC3 proved "
678 + "that a CNAME existed!");
684 // For cases 3 - 5, we need the proven closest encloser, and it can't
685 // match qname. Although, at this point, we know that it won't since we
686 // just checked that.
687 CEResponse ce = proveClosestEncloser(qname,
694 // At this point, not finding a match or a proven closest encloser is a
698 st_log.debug("proveNodata: did not match qname, "
699 + "nor found a proven closest encloser.");
706 Name wc = ceWildcard(ce.closestEncloser);
707 nsec3 = findMatchingNSEC3(hash(wc, nsec3params),
715 if (nsec3.hasType(qtype))
717 st_log.debug("proveNodata: matching wildcard had qtype!");
724 if (qtype != Type.DS)
726 st_log.debug("proveNodata: could not find matching NSEC3, "
727 + "nor matching wildcard, and qtype is not DS -- no more options.");
731 // We need to make sure that the covering NSEC3 is opt-in.
732 if (!ce.nc_nsec3.getOptInFlag())
734 st_log.debug("proveNodata: covering NSEC3 was not "
735 + "opt-in in an opt-in DS NOERROR/NODATA case.");
743 * Prove that a positive wildcard match was appropriate (no direct match
746 * @param nsec3s The NSEC3 records to work with.
747 * @param qname The qname that was matched to the wildard
748 * @param zonename The name of the zone that the NSEC3s come from.
749 * @param wildcard The purported wildcard that matched.
750 * @return true if the NSEC3 records prove this case.
752 public static boolean proveWildcard(List nsec3s, Name qname, Name zonename,
755 if (nsec3s == null || nsec3s.size() == 0) return false;
756 if (qname == null || wildcard == null) return false;
758 NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
759 if (nsec3params == null)
761 st_log.debug("couldn't find a single set of NSEC3 parameters (multiple parameters present).");
765 ByteArrayComparator bac = new ByteArrayComparator();
767 // We know what the (purported) closest encloser is by just looking at the
768 // supposed generating wildcard.
769 CEResponse candidate = new CEResponse(new Name(wildcard, 1), null);
771 // Now we still need to prove that the original data did not exist.
772 // Otherwise, we need to show that the next closer name is covered.
773 Name nextClosest = nextClosest(qname, candidate.closestEncloser);
774 candidate.nc_nsec3 = findCoveringNSEC3(hash(nextClosest, nsec3params),
780 if (candidate.nc_nsec3 == null)
782 st_log.debug("proveWildcard: did not find a covering NSEC3 "
783 + "that covered the next closer name to " + qname + " from "
784 + candidate.closestEncloser + " (derived from wildcard " + wildcard
793 * Prove that a DS response either had no DS, or wasn't a delegation point.
795 * Fundamentally there are two cases here: normal NODATA and Opt-In NODATA.
797 * @param nsec3s The NSEC3 RRs to examine.
798 * @param qname The name of the DS in question.
799 * @param zonename The name of the zone that the NSEC3 RRs come from.
801 * @return SecurityStatus.SECURE if it was proven that there is no DS in a
802 * secure (i.e., not opt-in) way, SecurityStatus.INSECURE if there
803 * was no DS in an insecure (i.e., opt-in) way,
804 * SecurityStatus.INDETERMINATE if it was clear that this wasn't a
805 * delegation point, and SecurityStatus.BOGUS if the proofs don't
808 public static int proveNoDS(List nsec3s, Name qname, Name zonename)
810 if (nsec3s == null || nsec3s.size() == 0) return SecurityStatus.BOGUS;
812 NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
813 if (nsec3params == null)
815 st_log.debug("couldn't find a single set of " +
816 "NSEC3 parameters (multiple parameters present).");
817 return SecurityStatus.BOGUS;
819 ByteArrayComparator bac = new ByteArrayComparator();
821 // Look for a matching NSEC3 to qname -- this is the normal NODATA case.
822 NSEC3Record nsec3 = findMatchingNSEC3(hash(qname, nsec3params),
830 // If the matching NSEC3 has the SOA bit set, it is from the wrong zone
831 // (the child instead of the parent). If it has the DS bit set, then we
833 if (nsec3.hasType(Type.SOA) || nsec3.hasType(Type.DS))
835 return SecurityStatus.BOGUS;
837 // If the NSEC3 RR doesn't have the NS bit set, then this wasn't a
839 if (!nsec3.hasType(Type.NS)) return SecurityStatus.INDETERMINATE;
841 // Otherwise, this proves no DS.
842 return SecurityStatus.SECURE;
845 // Otherwise, we are probably in the opt-in case.
846 CEResponse ce = proveClosestEncloser(qname,
854 return SecurityStatus.BOGUS;
857 // If we had the closest encloser proof, then we need to check that the
858 // covering NSEC3 was opt-in -- the proveClosestEncloser step already
859 // checked to see if the closest encloser was a delegation or DNAME.
860 if (ce.nc_nsec3.getOptInFlag())
862 return SecurityStatus.SECURE;
865 return SecurityStatus.BOGUS;