*
*/
-package se.rfc.unbound.validator;
+package se.rfc.unbound;
import java.util.*;
import java.io.*;
import java.security.*;
-import org.apache.log4j.Logger;
import org.xbill.DNS.*;
import org.xbill.DNS.security.*;
/**
* A class for performing basic DNSSEC verification. The DNSJAVA package
- * contains a similar class. This is a reimplementation that allows us to have
+ * contains a similar class. This is a re-implementation that allows us to have
* finer control over the validation process.
- *
- * @author davidb
- * @version $Revision$
*/
public class DnsSecVerifier
{
*/
private HashMap mAlgorithmMap;
- private Logger log = Logger.getLogger(this.getClass());
-
private static class AlgEntry
{
public String jcaName;
if (!mAlgorithmMap.containsKey(alg_orig))
{
- log.warn("Unable to alias " + alg_alias + " to unknown algorithm "
- + alg_orig);
+// log.warn("Unable to alias " + alg_alias + " to unknown algorithm "
+// + alg_orig);
continue;
}
if (mAlgorithmMap.containsKey(alg_alias))
{
- log.warn("Algorithm alias " + alg_alias
- + " is already defined and cannot be redefined");
+// log.warn("Algorithm alias " + alg_alias
+// + " is already defined and cannot be redefined");
continue;
}
{
Integer alg = (Integer) i.next();
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(alg);
- if (entry == null)
- log.warn("DNSSEC alg " + alg + " has a null entry!");
- else
- log.debug("DNSSEC alg " + alg + " maps to " + entry.jcaName
- + " (" + entry.dnssecAlg + ")");
+// if (entry == null)
+// log.warn("DNSSEC alg " + alg + " has a null entry!");
+// else
+// log.debug("DNSSEC alg " + alg + " maps to " + entry.jcaName
+// + " (" + entry.dnssecAlg + ")");
}
}
{
if (!signature.getSigner().equals(dnskey_rrset.getName()))
{
- log.trace("findKey: could not find appropriate key because "
- + "incorrect keyset was supplied. Wanted: " + signature.getSigner()
- + ", got: " + dnskey_rrset.getName());
+// log.trace("findKey: could not find appropriate key because "
+// + "incorrect keyset was supplied. Wanted: " + signature.getSigner()
+// + ", got: " + dnskey_rrset.getName());
return null;
}
if (res.size() == 0)
{
- log.trace("findKey: could not find a key matching "
- + "the algorithm and footprint in supplied keyset. ");
+// log.trace("findKey: could not find a key matching "
+// + "the algorithm and footprint in supplied keyset. ");
return null;
}
return res;
if (rrset == null || sigrec == null) return DNSSEC.Failed;
if (!rrset.getName().equals(sigrec.getName()))
{
- log.debug("Signature name does not match RRset name");
+// log.debug("Signature name does not match RRset name");
return SecurityStatus.BOGUS;
}
if (rrset.getType() != sigrec.getTypeCovered())
{
- log.debug("Signature type does not match RRset type");
+// log.debug("Signature type does not match RRset type");
return SecurityStatus.BOGUS;
}
Date expire = sigrec.getExpire();
if (now.before(start))
{
- log.debug("Signature is not yet valid");
+// log.debug("Signature is not yet valid");
return SecurityStatus.BOGUS;
}
if (now.after(expire))
{
- log.debug("Signature has expired (now = " + now + ", sig expires = "
- + expire);
+// log.debug("Signature has expired (now = " + now + ", sig expires = "
+// + expire);
return SecurityStatus.BOGUS;
}
if (pk == null)
{
- log.warn("Could not convert DNSKEY record to a JCA public key: "
- + key);
+// log.warn("Could not convert DNSKEY record to a JCA public key: "
+// + key);
return SecurityStatus.UNCHECKED;
}
}
if (!signer.verify(sig))
{
- log.info("Signature failed to verify cryptographically");
- log.debug("Failed signature: " + sigrec);
+// log.info("Signature failed to verify cryptographically");
+// log.debug("Failed signature: " + sigrec);
return SecurityStatus.BOGUS;
}
- log.trace("Signature verified: " + sigrec);
+// log.trace("Signature verified: " + sigrec);
return SecurityStatus.SECURE;
}
catch (IOException e)
{
- log.error("I/O error", e);
+// log.error("I/O error", e);
}
catch (GeneralSecurityException e)
{
- log.error("Security error", e);
+// log.error("Security error", e);
}
// FIXME: Since I'm not sure what would cause an exception here (failure
if (keys == null)
{
- log.trace("could not find appropriate key");
+// log.trace("could not find appropriate key");
return SecurityStatus.BOGUS;
}
if (!i.hasNext())
{
- log.info("RRset failed to verify due to lack of signatures");
+// log.info("RRset failed to verify due to lack of signatures");
return SecurityStatus.BOGUS;
}
if (res == SecurityStatus.SECURE) return res;
}
- log.info("RRset failed to verify: all signatures were BOGUS");
+// log.info("RRset failed to verify: all signatures were BOGUS");
return SecurityStatus.BOGUS;
}
Iterator i = rrset.sigs();
if (!i.hasNext())
{
- log.info("RRset failed to verify due to lack of signatures");
+// log.info("RRset failed to verify due to lack of signatures");
return SecurityStatus.BOGUS;
}
if (res == SecurityStatus.SECURE) return res;
}
- log.info("RRset failed to verify: all signatures were BOGUS");
+// log.info("RRset failed to verify: all signatures were BOGUS");
return SecurityStatus.BOGUS;
}
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(new Integer(algorithm));
if (entry == null)
{
- log.info("DNSSEC algorithm " + algorithm + " not recognized.");
+// log.info("DNSSEC algorithm " + algorithm + " not recognized.");
return null;
}
// TODO: should we cache the instance?
}
catch (NoSuchAlgorithmException e)
{
- log.error("error getting Signature object", e);
+// log.error("error getting Signature object", e);
}
return s;
import java.security.NoSuchAlgorithmException;
import java.util.*;
-import org.apache.log4j.Logger;
import org.xbill.DNS.*;
import org.xbill.DNS.utils.base32;
-import se.rfc.unbound.validator.DnsSecVerifier;
-import se.rfc.unbound.validator.SignUtils;
-import se.rfc.unbound.validator.SignUtils.ByteArrayComparator;
+import se.rfc.unbound.SignUtils.ByteArrayComparator;
+
public class NSEC3ValUtils
{
// parameters. The idea is to hash and compare for each group independently,
// instead of having to skip NSEC3 RRs with the wrong parameters.
- // The logger to use in static methods.
- private static Logger st_log = Logger.getLogger(NSEC3ValUtils.class);
private static Name asterisk_label = Name.fromConstantString("*");
}
catch (NoSuchAlgorithmException e)
{
- st_log.debug("Did not recognize hash algorithm: " + params.alg);
+// st_log.debug("Did not recognize hash algorithm: " + params.alg);
return null;
}
}
if (candidate == null)
{
- st_log.debug("proveClosestEncloser: could not find a "
- + "candidate for the closest encloser.");
+// st_log.debug("proveClosestEncloser: could not find a "
+// + "candidate for the closest encloser.");
return null;
}
{
if (proveDoesNotExist)
{
- st_log.debug("proveClosestEncloser: proved that qname existed!");
+// st_log.debug("proveClosestEncloser: proved that qname existed!");
return null;
}
// otherwise, we need to nothing else to prove that qname is its own
if (candidate.ce_nsec3.hasType(Type.NS)
&& !candidate.ce_nsec3.hasType(Type.SOA))
{
- st_log.debug("proveClosestEncloser: closest encloser "
- + "was a delegation!");
+// st_log.debug("proveClosestEncloser: closest encloser "
+// + "was a delegation!");
return null;
}
if (candidate.ce_nsec3.hasType(Type.DNAME))
{
- st_log.debug("proveClosestEncloser: closest encloser was a DNAME!");
+// st_log.debug("proveClosestEncloser: closest encloser was a DNAME!");
return null;
}
bac);
if (candidate.nc_nsec3 == null)
{
- st_log.debug("Could not find proof that the "
- + "closest encloser was the closest encloser");
+// st_log.debug("Could not find proof that the "
+// + "closest encloser was the closest encloser");
return null;
}
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
if (nsec3params == null)
{
- st_log.debug("Could not find a single set of " +
- "NSEC3 parameters (multiple parameters present).");
+// st_log.debug("Could not find a single set of " +
+// "NSEC3 parameters (multiple parameters present).");
return false;
}
if (ce == null)
{
- st_log.debug("proveNameError: failed to prove a closest encloser.");
+// st_log.debug("proveNameError: failed to prove a closest encloser.");
return false;
}
bac);
if (nsec3 == null)
{
- st_log.debug("proveNameError: could not prove that the "
- + "applicable wildcard did not exist.");
+// st_log.debug("proveNameError: could not prove that the "
+// + "applicable wildcard did not exist.");
return false;
}
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
if (nsec3params == null)
{
- st_log.debug("could not find a single set of "
- + "NSEC3 parameters (multiple parameters present)");
+// st_log.debug("could not find a single set of "
+// + "NSEC3 parameters (multiple parameters present)");
return false;
}
ByteArrayComparator bac = new ByteArrayComparator();
{
if (nsec3.hasType(qtype))
{
- st_log.debug("proveNodata: Matching NSEC3 proved that type existed!");
+// st_log.debug("proveNodata: Matching NSEC3 proved that type existed!");
return false;
}
if (nsec3.hasType(Type.CNAME))
{
- st_log.debug("proveNodata: Matching NSEC3 proved "
- + "that a CNAME existed!");
+// st_log.debug("proveNodata: Matching NSEC3 proved "
+// + "that a CNAME existed!");
return false;
}
return true;
// problem.
if (ce == null)
{
- st_log.debug("proveNodata: did not match qname, "
- + "nor found a proven closest encloser.");
+// st_log.debug("proveNodata: did not match qname, "
+// + "nor found a proven closest encloser.");
return false;
}
{
if (nsec3.hasType(qtype))
{
- st_log.debug("proveNodata: matching wildcard had qtype!");
+// st_log.debug("proveNodata: matching wildcard had qtype!");
return false;
}
return true;
// Case 5.
if (qtype != Type.DS)
{
- st_log.debug("proveNodata: could not find matching NSEC3, "
- + "nor matching wildcard, and qtype is not DS -- no more options.");
+// st_log.debug("proveNodata: could not find matching NSEC3, "
+// + "nor matching wildcard, and qtype is not DS -- no more options.");
return false;
}
// We need to make sure that the covering NSEC3 is opt-in.
if (!ce.nc_nsec3.getOptInFlag())
{
- st_log.debug("proveNodata: covering NSEC3 was not "
- + "opt-in in an opt-in DS NOERROR/NODATA case.");
+// st_log.debug("proveNodata: covering NSEC3 was not "
+// + "opt-in in an opt-in DS NOERROR/NODATA case.");
return false;
}
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
if (nsec3params == null)
{
- st_log.debug("couldn't find a single set of NSEC3 parameters (multiple parameters present).");
+// st_log.debug("couldn't find a single set of NSEC3 parameters (multiple parameters present).");
return false;
}
if (candidate.nc_nsec3 == null)
{
- st_log.debug("proveWildcard: did not find a covering NSEC3 "
- + "that covered the next closer name to " + qname + " from "
- + candidate.closestEncloser + " (derived from wildcard " + wildcard
- + ")");
+// st_log.debug("proveWildcard: did not find a covering NSEC3 "
+// + "that covered the next closer name to " + qname + " from "
+// + candidate.closestEncloser + " (derived from wildcard " + wildcard
+// + ")");
return false;
}
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
if (nsec3params == null)
{
- st_log.debug("couldn't find a single set of " +
- "NSEC3 parameters (multiple parameters present).");
+// st_log.debug("couldn't find a single set of " +
+// "NSEC3 parameters (multiple parameters present).");
return SecurityStatus.BOGUS;
}
ByteArrayComparator bac = new ByteArrayComparator();
h.setRcode(mHeader.getRcode());
for (int i = 0; i < 16; i++)
{
- if (Flags.isFlag(i)) h.setFlag(i, mHeader.getFlag(i));
+ if (Flags.isFlag(i)) {
+ if (mHeader.getFlag(i)) {
+ h.setFlag(i);
+ } else {
+ h.unsetFlag(i);
+ }
+ }
}
// Add all the records. -- this will set the counts correctly in the
public class SRRset extends RRset
{
private SecurityStatus mSecurityStatus;
-
+
/** Create a new, blank SRRset. */
public SRRset()
{
* contained RRsig records as well.
* @return The cloned SRRset.
*/
- public SRRset cloneSRRset(long withNewTTL)
- {
- SRRset nr = new SRRset();
-
- for (Iterator i = rrs(); i.hasNext();)
- {
- nr.addRR(((Record) i.next()).withTTL(withNewTTL));
- }
- for (Iterator i = sigs(); i.hasNext();)
- {
- nr.addRR(((Record) i.next()).withTTL(withNewTTL));
- }
-
- nr.mSecurityStatus = mSecurityStatus;
-
- return nr;
- }
+// public SRRset cloneSRRset(long withNewTTL)
+// {
+// SRRset nr = new SRRset();
+//
+// for (Iterator i = rrs(); i.hasNext();)
+// {
+// nr.addRR(((Record) i.next()).withTTL(withNewTTL));
+// }
+// for (Iterator i = sigs(); i.hasNext();)
+// {
+// nr.addRR(((Record) i.next()).withTTL(withNewTTL));
+// }
+//
+// nr.mSecurityStatus = mSecurityStatus;
+//
+// return nr;
+// }
public SRRset cloneSRRsetNoSigs()
{
return nr;
}
+
+
/**
* Return the current security status (generally: UNCHECKED, BOGUS, or
* SECURE).
* Set the current security status for this SRRset. This status will be
* shared amongst all copies of this SRRset (created with cloneSRRset())
*/
- public void setSecurityStatus(int status)
+ public void setSecurityStatus(byte status)
{
mSecurityStatus.setStatus(status);
}
+ public int totalSize() {
+ int num_sigs = 0;
+ for (Iterator i = sigs(); i.hasNext(); ) {
+ num_sigs++;
+ }
+ return size() + num_sigs;
+ }
+
/**
* @return The total number of records (data + sigs) in the SRRset.
*/
return totalSize();
}
+ public RRSIGRecord firstSig() {
+ for (Iterator i = sigs(); i.hasNext(); ) {
+ return (RRSIGRecord) i.next();
+ }
+ return null;
+ }
/**
* @return true if this RRset has RRSIG records that cover data records.
* (i.e., RRSIG SRRsets return false)
return sig.getSigner();
}
- public void setTTL(long ttl)
- {
- if (ttl < 0)
- {
- throw new IllegalArgumentException("ttl can't be less than zero, stupid! was " + ttl);
- }
- super.setTTL(ttl);
- }
+// public void setTTL(long ttl)
+// {
+// if (ttl < 0)
+// {
+// throw new IllegalArgumentException("ttl can't be less than zero, stupid! was " + ttl);
+// }
+// super.setTTL(ttl);
+// }
}
--- /dev/null
+/*
+ * $Id$
+ *
+ * Copyright (c) 2005 VeriSign, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+package se.rfc.unbound;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SignatureException;
+import java.security.interfaces.DSAParams;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.xbill.DNS.DNSKEYRecord;
+import org.xbill.DNS.DNSOutput;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.RRSIGRecord;
+import org.xbill.DNS.RRset;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.utils.base64;
+
+/**
+ * This class contains a bunch of utility methods that are generally useful in
+ * signing and verifying rrsets.
+ *
+ * @author David Blacka (original)
+ * @author $Author$
+ * @version $Revision$
+ */
+
+public class SignUtils
+{
+
+ /**
+ * This class implements a basic comparitor for byte arrays. It is primarily
+ * useful for comparing RDATA portions of DNS records in doing DNSSEC
+ * canonical ordering.
+ *
+ * @author David Blacka (original)
+ */
+ public static class ByteArrayComparator implements Comparator
+ {
+ private int mOffset = 0;
+ private boolean mDebug = false;
+
+ public ByteArrayComparator()
+ {
+ }
+
+ public ByteArrayComparator(int offset, boolean debug)
+ {
+ mOffset = offset;
+ mDebug = debug;
+ }
+
+ public int compare(Object o1, Object o2) throws ClassCastException
+ {
+ byte[] b1 = (byte[]) o1;
+ byte[] b2 = (byte[]) o2;
+
+ for (int i = mOffset; i < b1.length && i < b2.length; i++)
+ {
+ if (b1[i] != b2[i])
+ {
+ if (mDebug)
+ {
+ System.out.println("offset " + i + " differs (this is "
+ + (i - mOffset) + " bytes in from our offset.)");
+ }
+ return (b1[i] & 0xFF) - (b2[i] & 0xFF);
+ }
+ }
+
+ return b1.length - b2.length;
+ }
+ }
+
+ // private static final int DSA_SIGNATURE_LENGTH = 20;
+ private static final int ASN1_INT = 0x02;
+ private static final int ASN1_SEQ = 0x30;
+
+ public static final int RR_NORMAL = 0;
+ public static final int RR_DELEGATION = 1;
+ public static final int RR_GLUE = 2;
+ public static final int RR_INVALID = 3;
+
+ /**
+ * Generate from some basic information a prototype SIG RR containing
+ * everything but the actual signature itself.
+ *
+ * @param rrset the RRset being signed.
+ * @param signer the name of the signing key
+ * @param alg the algorithm of the signing key
+ * @param keyid the keyid (or footprint) of the signing key
+ * @param start the SIG inception time.
+ * @param expire the SIG expiration time.
+ * @param sig_ttl the TTL of the resulting SIG record.
+ * @return a prototype signature based on the RRset and key information.
+ */
+ public static RRSIGRecord generatePreRRSIG(RRset rrset, Name signer,
+ int alg, int keyid, Date start, Date expire, long sig_ttl)
+ {
+ return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl, rrset
+ .getType(), alg, rrset.getTTL(), expire, start, keyid, signer, null);
+ }
+
+ /**
+ * Generate from some basic information a prototype SIG RR containing
+ * everything but the actual signature itself.
+ *
+ * @param rrset the RRset being signed.
+ * @param key the public KEY RR counterpart to the key being used to sign
+ * the RRset
+ * @param start the SIG inception time.
+ * @param expire the SIG expiration time.
+ * @param sig_ttl the TTL of the resulting SIG record.
+ * @return a prototype signature based on the RRset and key information.
+ */
+ public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
+ Date start, Date expire, long sig_ttl)
+ {
+ return generatePreRRSIG(rrset, key.getName(), key.getAlgorithm(), key
+ .getFootprint(), start, expire, sig_ttl);
+ }
+
+ /**
+ * Generate from some basic information a prototype SIG RR containing
+ * everything but the actual signature itself.
+ *
+ * @param rec the DNS record being signed (forming an entire RRset).
+ * @param key the public KEY RR counterpart to the key signing the record.
+ * @param start the SIG inception time.
+ * @param expire the SIG expiration time.
+ * @param sig_ttl the TTL of the result SIG record.
+ * @return a prototype signature based on the Record and key information.
+ */
+ public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key,
+ Date start, Date expire, long sig_ttl)
+ {
+ return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl, rec
+ .getType(), key.getAlgorithm(), rec.getTTL(), expire, start, key
+ .getFootprint(), key.getName(), null);
+ }
+
+ /**
+ * Generate the binary image of the prototype SIG RR.
+ *
+ * @param presig the SIG RR prototype.
+ * @return the RDATA portion of the prototype SIG record. This forms the
+ * first part of the data to be signed.
+ */
+ private static byte[] generatePreSigRdata(RRSIGRecord presig)
+ {
+ // Generate the binary image;
+ DNSOutput image = new DNSOutput();
+
+ // precalc some things
+ int start_time = (int) (presig.getTimeSigned().getTime() / 1000);
+ int expire_time = (int) (presig.getExpire().getTime() / 1000);
+ Name signer = presig.getSigner();
+
+ // first write out the partial SIG record (this is the SIG RDATA
+ // minus the actual signature.
+ image.writeU16(presig.getTypeCovered());
+ image.writeU8(presig.getAlgorithm());
+ image.writeU8(presig.getLabels());
+ image.writeU32((int) presig.getOrigTTL());
+ image.writeU32(expire_time);
+ image.writeU32(start_time);
+ image.writeU16(presig.getFootprint());
+ image.writeByteArray(signer.toWireCanonical());
+
+ return image.toByteArray();
+ }
+
+ /**
+ * Calculate the canonical wire line format of the RRset.
+ *
+ * @param rrset the RRset to convert.
+ * @param ttl the TTL to use when canonicalizing -- this is generally the
+ * TTL of the signature if there is a pre-existing signature. If
+ * not it is just the ttl of the rrset itself.
+ * @param labels the labels field of the signature, or 0.
+ * @return the canonical wire line format of the rrset. This is the second
+ * part of data to be signed.
+ */
+ public static byte[] generateCanonicalRRsetData(RRset rrset, long ttl,
+ int labels)
+ {
+ DNSOutput image = new DNSOutput();
+
+ if (ttl == 0) ttl = rrset.getTTL();
+ Name n = rrset.getName();
+ if (labels == 0)
+ {
+ labels = n.labels();
+ }
+ else
+ {
+ // correct for Name()'s conception of label count.
+ labels++;
+ }
+ boolean wildcardName = false;
+ if (n.labels() != labels)
+ {
+ n = n.wild(n.labels() - labels);
+ wildcardName = true;
+// log.trace("Detected wildcard expansion: " + rrset.getName() + " changed to " + n);
+ }
+
+ // now convert load the wire format records in the RRset into a
+ // list of byte arrays.
+ ArrayList canonical_rrs = new ArrayList();
+ for (Iterator i = rrset.rrs(); i.hasNext();)
+ {
+ Record r = (Record) i.next();
+ if (r.getTTL() != ttl || wildcardName)
+ {
+ // If necessary, we need to create a new record with a new ttl or ownername.
+ // In the TTL case, this avoids changing the ttl in the response.
+ r = Record.newRecord(n, r.getType(), r.getDClass(), ttl, r
+ .rdataToWireCanonical());
+ }
+ byte[] wire_fmt = r.toWireCanonical();
+ canonical_rrs.add(wire_fmt);
+ }
+
+ // put the records into the correct ordering.
+ // Caculate the offset where the RDATA begins (we have to skip
+ // past the length byte)
+
+ int offset = rrset.getName().toWireCanonical().length + 10;
+ ByteArrayComparator bac = new ByteArrayComparator(offset, false);
+
+ Collections.sort(canonical_rrs, bac);
+
+ for (Iterator i = canonical_rrs.iterator(); i.hasNext();)
+ {
+ byte[] wire_fmt_rec = (byte[]) i.next();
+ image.writeByteArray(wire_fmt_rec);
+ }
+
+ return image.toByteArray();
+ }
+
+ /**
+ * Given an RRset and the prototype signature, generate the canonical data
+ * that is to be signed.
+ *
+ * @param rrset the RRset to be signed.
+ * @param presig a prototype SIG RR created using the same RRset.
+ * @return a block of data ready to be signed.
+ */
+ public static byte[] generateSigData(RRset rrset, RRSIGRecord presig)
+ throws IOException
+ {
+ byte[] rrset_data = generateCanonicalRRsetData(rrset, presig.getOrigTTL(), presig.getLabels());
+
+ return generateSigData(rrset_data, presig);
+ }
+
+ /**
+ * Given an RRset and the prototype signature, generate the canonical data
+ * that is to be signed.
+ *
+ * @param rrset_data the RRset converted into canonical wire line format (as
+ * per the canonicalization rules in RFC 2535).
+ * @param presig the prototype signature based on the same RRset represented
+ * in <code>rrset_data</code>.
+ * @return a block of data ready to be signed.
+ */
+ public static byte[] generateSigData(byte[] rrset_data, RRSIGRecord presig)
+ throws IOException
+ {
+ byte[] sig_rdata = generatePreSigRdata(presig);
+
+ ByteArrayOutputStream image = new ByteArrayOutputStream(sig_rdata.length
+ + rrset_data.length);
+
+ image.write(sig_rdata);
+ image.write(rrset_data);
+
+ return image.toByteArray();
+ }
+
+ /**
+ * Given the acutal signature an the prototype signature, combine them and
+ * return the fully formed SIGRecord.
+ *
+ * @param signature the cryptographic signature, in DNSSEC format.
+ * @param presig the prototype SIG RR to add the signature to.
+ * @return the fully formed SIG RR.
+ */
+ public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig)
+ {
+ return new RRSIGRecord(presig.getName(), presig.getDClass(), presig
+ .getTTL(), presig.getTypeCovered(), presig.getAlgorithm(), presig
+ .getOrigTTL(), presig.getExpire(), presig.getTimeSigned(), presig
+ .getFootprint(), presig.getSigner(), signature);
+ }
+
+ /**
+ * Converts from a RFC 2536 formatted DSA signature to a JCE (ASN.1)
+ * formatted signature.
+ *
+ * <p>
+ * ASN.1 format = ASN1_SEQ . seq_length . ASN1_INT . Rlength . R . ANS1_INT .
+ * Slength . S
+ * </p>
+ *
+ * The integers R and S may have a leading null byte to force the integer
+ * positive.
+ *
+ * @param signature the RFC 2536 formatted DSA signature.
+ * @return The ASN.1 formatted DSA signature.
+ * @throws SignatureException if there was something wrong with the RFC 2536
+ * formatted signature.
+ */
+ public static byte[] convertDSASignature(byte[] signature)
+ throws SignatureException
+ {
+ if (signature.length != 41)
+ throw new SignatureException("RFC 2536 signature not expected length.");
+
+ byte r_pad = 0;
+ byte s_pad = 0;
+
+ // handle initial null byte padding.
+ if (signature[1] < 0) r_pad++;
+ if (signature[21] < 0) s_pad++;
+
+ // ASN.1 length = R length + S length + (2 + 2 + 2), where each 2
+ // is for a ASN.1 type-length byte pair of which there are three
+ // (SEQ, INT, INT).
+ byte sig_length = (byte) (40 + r_pad + s_pad + 6);
+
+ byte sig[] = new byte[sig_length];
+ byte pos = 0;
+
+ sig[pos++] = ASN1_SEQ;
+ sig[pos++] = (byte) (sig_length - 2); // all but the SEQ type+length.
+ sig[pos++] = ASN1_INT;
+ sig[pos++] = (byte) (20 + r_pad);
+
+ // copy the value of R, leaving a null byte if necessary
+ if (r_pad == 1) sig[pos++] = 0;
+
+ System.arraycopy(signature, 1, sig, pos, 20);
+ pos += 20;
+
+ sig[pos++] = ASN1_INT;
+ sig[pos++] = (byte) (20 + s_pad);
+
+ // copy the value of S, leaving a null byte if necessary
+ if (s_pad == 1) sig[pos++] = 0;
+
+ System.arraycopy(signature, 21, sig, pos, 20);
+
+ return sig;
+ }
+
+ /**
+ * Converts from a JCE (ASN.1) formatted DSA signature to a RFC 2536
+ * compliant signature.
+ *
+ * <p>
+ * rfc2536 format = T . R . S
+ * </p>
+ *
+ * where T is a number between 0 and 8, which is based on the DSA key
+ * length, and R & S are formatted to be exactly 20 bytes each (no leading
+ * null bytes).
+ *
+ * @param params the DSA parameters associated with the DSA key used to
+ * generate the signature.
+ * @param signature the ASN.1 formatted DSA signature.
+ * @return a RFC 2536 formatted DSA signature.
+ * @throws SignatureException if something is wrong with the ASN.1 format.
+ */
+ public static byte[] convertDSASignature(DSAParams params, byte[] signature)
+ throws SignatureException
+ {
+ if (signature[0] != ASN1_SEQ || signature[2] != ASN1_INT)
+ {
+ throw new SignatureException(
+ "Invalid ASN.1 signature format: expected SEQ, INT");
+ }
+
+ byte r_pad = (byte) (signature[3] - 20);
+
+ if (signature[24 + r_pad] != ASN1_INT)
+ {
+ throw new SignatureException(
+ "Invalid ASN.1 signature format: expected SEQ, INT, INT");
+ }
+
+// log.trace("(start) ASN.1 DSA Sig:\n" + base64.toString(signature));
+
+ byte s_pad = (byte) (signature[25 + r_pad] - 20);
+
+ byte[] sig = new byte[41]; // all rfc2536 signatures are 41 bytes.
+
+ // Calculate T:
+ sig[0] = (byte) ((params.getP().bitLength() - 512) / 64);
+
+ // copy R value
+ if (r_pad >= 0)
+ {
+ System.arraycopy(signature, 4 + r_pad, sig, 1, 20);
+ }
+ else
+ {
+ // R is shorter than 20 bytes, so right justify the number
+ // (r_pad is negative here, remember?).
+ Arrays.fill(sig, 1, 1 - r_pad, (byte) 0);
+ System.arraycopy(signature, 4, sig, 1 - r_pad, 20 + r_pad);
+ }
+
+ // copy S value
+ if (s_pad >= 0)
+ {
+ System.arraycopy(signature, 26 + r_pad + s_pad, sig, 21, 20);
+ }
+ else
+ {
+ // S is shorter than 20 bytes, so right justify the number
+ // (s_pad is negative here).
+ Arrays.fill(sig, 21, 21 - s_pad, (byte) 0);
+ System.arraycopy(signature, 26 + r_pad, sig, 21 - s_pad, 20 + s_pad);
+ }
+
+// if (r_pad < 0 || s_pad < 0)
+// {
+// log.trace("(finish ***) RFC 2536 DSA Sig:\n" + base64.toString(sig));
+//
+// }
+// else
+// {
+// log.trace("(finish) RFC 2536 DSA Sig:\n" + base64.toString(sig));
+// }
+
+ return sig;
+ }
+}
*
*/
-package se.rfc.unbound.validator;
+package se.rfc.unbound;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
-import org.apache.log4j.Logger;
import org.xbill.DNS.*;
-import se.rfc.unbound.*;
-
/**
* This is a collection of routines encompassing the logic of validating
* different message types.
/** A response to a qtype=ANY query. */
public static final int ANY = 6;
- private Logger log = Logger.getLogger(this.getClass());
- private static Logger st_log = Logger.getLogger(ValUtils.class);
-
/** A local copy of the verifier object. */
private DnsSecVerifier mVerifier;
if (rrsets[i].getType() == Type.CNAME) return CNAME;
}
- st_log.warn("Failed to classify response message:\n" + m);
+// st_log.warn("Failed to classify response message:\n" + m);
return UNKNOWN;
}
* @return a signer name, if the response is signed (even partially), or
* null if the response isn't signed.
*/
- public Name findSigner(SMessage m, Request request)
+ public Name findSigner(SMessage m)
{
int subtype = classifyResponse(m);
- Name qname = request.getQName();
+ Name qname = m.getQName();
SRRset[] rrsets;
}
return null;
default :
- log.debug("findSigner: could not find signer name "
- + "for unknown type response.");
+// log.debug("findSigner: could not find signer name "
+// + "for unknown type response.");
return null;
}
}
* this sort of thing is checked before fetching the matching DNSKEY
* rrset.
*/
- public KeyEntry verifyNewDNSKEYs(SRRset dnskey_rrset, SRRset ds_rrset)
- {
- if (!dnskey_rrset.getName().equals(ds_rrset.getName()))
- {
- log.debug("DNSKEY RRset did not match DS RRset by name!");
- return KeyEntry
- .newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
- }
-
- // as long as this is false, we can consider this DS rrset to be
- // equivalent to no DS rrset.
- boolean hasUsefulDS = false;
-
- for (Iterator i = ds_rrset.rrs(); i.hasNext();)
- {
- DSRecord ds = (DSRecord) i.next();
-
- // Check to see if we can understand this DS.
- if (!supportsDigestID(ds.getDigestID())
- || !mVerifier.supportsAlgorithm(ds.getAlgorithm()))
- {
- continue;
- }
-
- // Once we see a single DS with a known digestID and algorithm, we
- // cannot return INSECURE (with a "null" KeyEntry).
- hasUsefulDS = true;
-
- DNSKEY : for (Iterator j = dnskey_rrset.rrs(); j.hasNext();)
- {
- DNSKEYRecord dnskey = (DNSKEYRecord) j.next();
-
- // Skip DNSKEYs that don't match the basic criteria.
- if (ds.getFootprint() != dnskey.getFootprint()
- || ds.getAlgorithm() != dnskey.getAlgorithm())
- {
- continue;
- }
-
- // Convert the candidate DNSKEY into a hash using the same DS hash
- // algorithm.
- byte[] key_hash = calculateDSHash(dnskey, ds.getDigestID());
- byte[] ds_hash = ds.getDigest();
-
- // see if there is a length mismatch (unlikely)
- if (key_hash.length != ds_hash.length)
- {
- continue DNSKEY;
- }
-
- for (int k = 0; k < key_hash.length; k++)
- {
- if (key_hash[k] != ds_hash[k]) continue DNSKEY;
- }
-
- // Otherwise, we have a match! Make sure that the DNSKEY verifies
- // *with this key*.
- byte res = mVerifier.verify(dnskey_rrset, dnskey);
- if (res == SecurityStatus.SECURE)
- {
- log.trace("DS matched DNSKEY.");
- dnskey_rrset.setSecurityStatus(SecurityStatus.SECURE);
- return KeyEntry.newKeyEntry(dnskey_rrset);
- }
- // If it didn't validate with the DNSKEY, try the next one!
- }
- }
-
- // None of the DS's worked out.
-
- // If no DSs were understandable, then this is OK.
- if (!hasUsefulDS)
- {
- log.debug("No usuable DS records were found -- treating as insecure.");
- return KeyEntry.newNullKeyEntry(ds_rrset.getName(), ds_rrset
- .getDClass(), ds_rrset.getTTL());
- }
- // If any were understandable, then it is bad.
- log.debug("Failed to match any usable DS to a DNSKEY.");
- return KeyEntry.newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
- }
+// public KeyEntry verifyNewDNSKEYs(SRRset dnskey_rrset, SRRset ds_rrset)
+// {
+// if (!dnskey_rrset.getName().equals(ds_rrset.getName()))
+// {
+//// log.debug("DNSKEY RRset did not match DS RRset by name!");
+// return KeyEntry
+// .newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
+// }
+//
+// // as long as this is false, we can consider this DS rrset to be
+// // equivalent to no DS rrset.
+// boolean hasUsefulDS = false;
+//
+// for (Iterator i = ds_rrset.rrs(); i.hasNext();)
+// {
+// DSRecord ds = (DSRecord) i.next();
+//
+// // Check to see if we can understand this DS.
+// if (!supportsDigestID(ds.getDigestID())
+// || !mVerifier.supportsAlgorithm(ds.getAlgorithm()))
+// {
+// continue;
+// }
+//
+// // Once we see a single DS with a known digestID and algorithm, we
+// // cannot return INSECURE (with a "null" KeyEntry).
+// hasUsefulDS = true;
+//
+// DNSKEY : for (Iterator j = dnskey_rrset.rrs(); j.hasNext();)
+// {
+// DNSKEYRecord dnskey = (DNSKEYRecord) j.next();
+//
+// // Skip DNSKEYs that don't match the basic criteria.
+// if (ds.getFootprint() != dnskey.getFootprint()
+// || ds.getAlgorithm() != dnskey.getAlgorithm())
+// {
+// continue;
+// }
+//
+// // Convert the candidate DNSKEY into a hash using the same DS hash
+// // algorithm.
+// byte[] key_hash = calculateDSHash(dnskey, ds.getDigestID());
+// byte[] ds_hash = ds.getDigest();
+//
+// // see if there is a length mismatch (unlikely)
+// if (key_hash.length != ds_hash.length)
+// {
+// continue DNSKEY;
+// }
+//
+// for (int k = 0; k < key_hash.length; k++)
+// {
+// if (key_hash[k] != ds_hash[k]) continue DNSKEY;
+// }
+//
+// // Otherwise, we have a match! Make sure that the DNSKEY verifies
+// // *with this key*.
+// byte res = mVerifier.verify(dnskey_rrset, dnskey);
+// if (res == SecurityStatus.SECURE)
+// {
+//// log.trace("DS matched DNSKEY.");
+// dnskey_rrset.setSecurityStatus(SecurityStatus.SECURE);
+// return KeyEntry.newKeyEntry(dnskey_rrset);
+// }
+// // If it didn't validate with the DNSKEY, try the next one!
+// }
+// }
+//
+// // None of the DS's worked out.
+//
+// // If no DSs were understandable, then this is OK.
+// if (!hasUsefulDS)
+// {
+//// log.debug("No usuable DS records were found -- treating as insecure.");
+// return KeyEntry.newNullKeyEntry(ds_rrset.getName(), ds_rrset
+// .getDClass(), ds_rrset.getTTL());
+// }
+// // If any were understandable, then it is bad.
+//// log.debug("Failed to match any usable DS to a DNSKEY.");
+// return KeyEntry.newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
+// }
/**
* Given a DNSKEY record, generate the DS record from it.
md = MessageDigest.getInstance("SHA");
return md.digest(os.toByteArray());
case DSRecord.SHA256_DIGEST_ID:
- SHA256 sha = new SHA256();
- sha.setData(os.toByteArray());
- return sha.getDigest();
+ md = MessageDigest.getInstance("SHA256");
+ return md.digest(os.toByteArray());
default :
- st_log.warn("Unknown DS algorithm: " + ds_alg);
+// st_log.warn("Unknown DS algorithm: " + ds_alg);
return null;
}
}
catch (NoSuchAlgorithmException e)
{
- st_log.error("Error using DS algorithm: " + ds_alg, e);
+// st_log.error("Error using DS algorithm: " + ds_alg, e);
return null;
}
}
* @param rrset The SRRset to update.
* @param security The security status.
*/
- public static void setRRsetSecurity(SRRset rrset, int security)
+ public static void setRRsetSecurity(SRRset rrset, byte security)
{
if (rrset == null) return;
* (ans_rrset.getSecurityStatus() != SecurityStatus.SECURE) { return; }
* key_rrset = ke.getRRset();
*/
- public static void setMessageSecurity(SMessage m, int security)
+ public static void setMessageSecurity(SMessage m, byte security)
{
if (m == null) return;
if (rrset.getSecurityStatus() == SecurityStatus.SECURE)
{
- log.trace("verifySRRset: rrset <" + rrset_name
- + "> previously found to be SECURE");
+// log.trace("verifySRRset: rrset <" + rrset_name
+// + "> previously found to be SECURE");
return SecurityStatus.SECURE;
}
byte status = mVerifier.verify(rrset, key_rrset);
if (status != SecurityStatus.SECURE)
{
- log.debug("verifySRRset: rrset <" + rrset_name + "> found to be BAD");
+// log.debug("verifySRRset: rrset <" + rrset_name + "> found to be BAD");
status = SecurityStatus.BOGUS;
}
- else
- {
- log.trace("verifySRRset: rrset <" + rrset_name + "> found to be SECURE");
- }
+// else
+// {
+// log.trace("verifySRRset: rrset <" + rrset_name + "> found to be SECURE");
+// }
rrset.setSecurityStatus(status);
return status;
return false;
}
+ private static RRSIGRecord rrsetFirstSig(RRset rrset) {
+ for (Iterator i = rrset.sigs(); i.hasNext(); ) {
+ return (RRSIGRecord) i.next();
+ }
+ return null;
+ }
/**
* Determine by looking at a signed RRset whether or not the rrset name was
* the result of a wildcard expansion.
public static boolean rrsetIsWildcardExpansion(RRset rrset)
{
if (rrset == null) return false;
- RRSIGRecord rrsig = (RRSIGRecord) rrset.firstSig();
+ RRSIGRecord rrsig = rrsetFirstSig(rrset);
if (rrset.getName().labels() - 1 > rrsig.getLabels())
{
public static Name rrsetWildcard(RRset rrset)
{
if (rrset == null) return null;
- RRSIGRecord rrsig = (RRSIGRecord) rrset.firstSig();
+ RRSIGRecord rrsig = rrsetFirstSig(rrset);
// if the RRSIG label count is shorter than the number of actual labels,
// then this rrset was synthesized from a wildcard.