NSEC3 support, remove plain opt-in support until private algs work

git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@35 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e
This commit is contained in:
David Blacka 2005-10-27 21:50:54 +00:00
parent ab479a3e7b
commit 04ab26f434
4 changed files with 755 additions and 135 deletions

Binary file not shown.

View File

@ -28,13 +28,8 @@ import java.io.PrintWriter;
import java.security.GeneralSecurityException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.TimeZone;
import java.util.*;
import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.Level;
@ -47,6 +42,7 @@ import org.xbill.DNS.RRset;
import org.xbill.DNS.Record;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type;
import org.xbill.DNS.utils.base16;
import com.verisignlabs.dnssec.security.BINDKeyUtils;
import com.verisignlabs.dnssec.security.DnsKeyPair;
@ -68,8 +64,7 @@ public class SignZone
private static Logger log;
/**
* This is a small inner class used to hold all of the command line option
* state.
* This is an inner class used to hold all of the command line option state.
*/
private static class CLIState
{
@ -85,9 +80,11 @@ public class SignZone
public boolean verifySigs = false;
public boolean selfSignKeys = true;
public boolean useOptIn = false;
public boolean optInConserve = false;
public boolean fullySignKeyset = false;
public List includeNames = null;
public boolean useNsec3 = false;
public byte[] salt = null;
public int iterations = 0;
public CLIState()
{
@ -115,23 +112,25 @@ public class SignZone
case 0 :
rootLogger.setLevel(Level.OFF);
break;
case 5 :
case 4 :
default :
rootLogger.setLevel(Level.INFO);
break;
case 5 :
rootLogger.setLevel(Level.FINE);
break;
case 6 :
rootLogger.setLevel(Level.ALL);
break;
}
Handler[] handlers = rootLogger.getHandlers();
for (int i = 0; i < handlers.length; i++)
handlers[i].setLevel(rootLogger.getLevel());
}
if (cli.hasOption('a')) verifySigs = true;
if (cli.hasOption('3')) useNsec3 = true;
if (cli.hasOption('O')) useOptIn = true;
if (cli.hasOption('C'))
{
useOptIn = true;
optInConserve = true;
}
if (cli.hasOption('F')) fullySignKeyset = true;
@ -185,6 +184,21 @@ public class SignZone
includeNames = getNameList(includeNamesFile);
}
if ((optstr = cli.getOptionValue("salt")) != null)
{
salt = base16.fromString(optstr);
}
if ((optstr = cli.getOptionValue("random-salt")) != null)
{
int length = parseInt(optstr, 0);
if (length > 0 && length <= 255)
{
Random random = new Random();
salt = new byte[length];
random.nextBytes(salt);
}
}
String[] files = cli.getArgs();
if (files.length < 2)
@ -209,73 +223,60 @@ public class SignZone
// boolean options
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("a", false, "verify generated signatures>");
opts.addOption("a", "verify", false, "verify generated signatures>");
opts.addOption("F",
"fully-sign-keyset",
false,
"sign the zone apex keyset with all "
+ "available keys, instead of just key-signing-keys.");
// Opt-In generation switches
OptionGroup opt_in_opts = new OptionGroup();
opt_in_opts.addOption(new Option("O", "generate a fully Opt-In zone."));
opt_in_opts.addOption(new Option("C",
"generate a conservative Opt-In zone."));
opts.addOptionGroup(opt_in_opts);
"sign the zone apex keyset with all available keys.");
// Argument options
OptionBuilder.hasOptionalArg();
OptionBuilder.withLongOpt("verbose");
OptionBuilder.withArgName("level");
OptionBuilder.withDescription("verbosity level -- 0 is silence, "
+ "5 is debug information, " + "6 is trace information. "
+ "No argument means 5.");
opts.addOption(OptionBuilder.create('v'));
opts.addOption(OptionBuilder.hasOptionalArg().withLongOpt("verbose")
.withArgName("level").withDescription("verbosity level")
.create('v'));
opts.addOption(OptionBuilder.hasArg().withArgName("dir")
.withLongOpt("keyset-directory")
.withDescription("directory to find keyset files (default '.').")
.create('d'));
opts.addOption(OptionBuilder.hasArg().withArgName("dir")
.withLongOpt("key-directory")
.withDescription("directory to find key files (default '.').")
.create('D'));
opts.addOption(OptionBuilder.hasArg().withArgName("time/offset")
.withLongOpt("start-time")
.withDescription("signature starting time "
+ "(default is now - 1 hour)").create('s'));
opts.addOption(OptionBuilder.hasArg().withArgName("time/offset")
.withLongOpt("expire-time")
.withDescription("signature expiration time "
+ "(default is start-time + 30 days).").create('e'));
opts.addOption(OptionBuilder.hasArg().withArgName("outfile")
.withDescription("file the signed zone is written to "
+ "(default is <origin>.signed).").create('f'));
opts.addOption(OptionBuilder.hasArgs().withArgName("KSK file")
.withLongOpt("ksk-file").withDescription("this key is a key "
+ "signing key (may repeat).").create('k'));
opts.addOption(OptionBuilder.hasArg().withArgName("file")
.withLongOpt("include-file")
.withDescription("include names in this "
+ "file in the NSEC/NSEC3 chain.").create('I'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("dir");
OptionBuilder.withLongOpt("keyset-directory");
OptionBuilder.withDescription("directory to find keyset files (default '.').");
opts.addOption(OptionBuilder.create('d'));
// NSEC3 options
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
opts.addOption("O",
"use-opt-in",
false,
"generate a fully Opt-In zone.");
OptionBuilder.hasArg();
OptionBuilder.withArgName("dir");
OptionBuilder.withLongOpt("key-directory");
OptionBuilder.withDescription("directory to find key files (default '.').");
opts.addOption(OptionBuilder.create('D'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("time/offset");
OptionBuilder.withLongOpt("start-time");
OptionBuilder.withDescription("signature starting time (default is now - 1 hour)");
opts.addOption(OptionBuilder.create('s'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("time/offset");
OptionBuilder.withLongOpt("expire-time");
OptionBuilder.withDescription("signature expiration time (default is "
+ "start-time + 30 days");
opts.addOption(OptionBuilder.create('e'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("outfile");
OptionBuilder.withDescription("file the signed zone is written "
+ "to (default is <origin>.signed).");
opts.addOption(OptionBuilder.create('f'));
OptionBuilder.hasArgs();
OptionBuilder.withArgName("KSK file");
OptionBuilder.withLongOpt("ksk-file");
OptionBuilder.withDescription("this key is a key signing key "
+ "(may repeat)");
opts.addOption(OptionBuilder.create('k'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("file");
OptionBuilder.withLongOpt("include-file");
OptionBuilder.withDescription("include names in this file "
+ "in the NSEC chain");
opts.addOption(OptionBuilder.create('I'));
opts.addOption(OptionBuilder.hasArg().withLongOpt("salt")
.withArgName("hex value").withDescription("supply a salt value.")
.create());
opts.addOption(OptionBuilder.hasArg().withLongOpt("random-salt")
.withArgName("length").withDescription("generate a random salt.")
.create());
opts.addOption(OptionBuilder.hasArg().withLongOpt("iterations")
.withArgName("value")
.withDescription("use this value for the iterations in NSEC3.")
.create());
}
/** Print out the usage and help statements, then quit. */
@ -286,7 +287,8 @@ public class SignZone
PrintWriter out = new PrintWriter(System.err);
// print our own usage statement:
out.println("usage: signZone.sh [..options..] zone_file [key_file ...] ");
out.println("usage: signZone.sh [..options..] "
+ "zone_file [key_file ...] ");
f.printHelp(out,
75,
"signZone.sh",
@ -572,10 +574,8 @@ public class SignZone
toList.add(i.next());
}
int type = SignUtils.recordSecType(zonename,
rrset.getName(),
rrset.getType(),
last_cut);
int type = SignUtils.recordSecType(zonename, rrset.getName(), rrset
.getType(), last_cut);
// we don't sign non-normal sets (delegations, glue, invalid).
// we also don't sign the zone key set unless we've been asked.
@ -595,7 +595,8 @@ public class SignZone
// otherwise we will just sign them with the zonesigning keys.
if (keysigningkeypairs != null && keysigningkeypairs.size() > 0)
{
List sigs = signer.signRRset(rrset, keysigningkeypairs, start, expire);
List sigs = signer
.signRRset(rrset, keysigningkeypairs, start, expire);
toList.addAll(sigs);
// If we aren't going to sign with all the keys, bail out now.
@ -639,13 +640,13 @@ public class SignZone
*/
private static List signZone(JCEDnsSecSigner signer, Name zonename,
List records, List keysigningkeypairs, List zonekeypairs, Date start,
Date expire, boolean useOptIn, boolean useConservativeOptIn,
boolean fullySignKeyset, List NSECIncludeNames) throws IOException,
GeneralSecurityException
Date expire, boolean fullySignKeyset)
throws IOException, GeneralSecurityException
{
// Remove any existing DNSSEC records (NSEC, RRSIG)
// Remove any existing DNSSEC records (NSEC, NSEC3, RRSIG)
SignUtils.removeGeneratedRecords(zonename, records);
// Sort the zone
Collections.sort(records, new RecordComparator());
@ -655,18 +656,8 @@ public class SignZone
// Generate DS records
SignUtils.generateDSRecords(zonename, records);
// Generate NXT records
if (useOptIn)
{
SignUtils.generateOptInNSECRecords(zonename,
records,
NSECIncludeNames,
useConservativeOptIn);
}
else
{
SignUtils.generateNSECRecords(zonename, records);
}
// Generate the NSEC records
SignUtils.generateNSECRecords(zonename, records);
// Assemble into RRsets and sign.
RRset rrset = new RRset();
@ -678,7 +669,102 @@ public class SignZone
Record r = (Record) i.next();
// First record
if (rrset.getName() == null)
if (rrset.size() == 0)
{
rrset.addRR(r);
continue;
}
// Current record is part of the current RRset.
if (rrset.getName().equals(r.getName())
&& rrset.getDClass() == r.getDClass()
&& rrset.getType() == r.getType())
{
rrset.addRR(r);
continue;
}
// Otherwise, we have completed the RRset
// Sign the records
// add the RRset to the list of signed_records, regardless of
// whether or not we actually end up signing the set.
last_cut = addRRset(signer,
signed_records,
zonename,
rrset,
keysigningkeypairs,
zonekeypairs,
start,
expire,
fullySignKeyset,
last_cut);
rrset.clear();
rrset.addRR(r);
}
// add the last RR set
addRRset(signer,
signed_records,
zonename,
rrset,
keysigningkeypairs,
zonekeypairs,
start,
expire,
fullySignKeyset,
last_cut);
return signed_records;
}
private static List signZoneNSEC3(JCEDnsSecSigner signer, Name zonename,
List records, List keysigningkeypairs, List zonekeypairs, Date start,
Date expire, boolean fullySignKeyset, boolean useOptIn,
List includedNames, byte[] salt, int iterations)
throws IOException, GeneralSecurityException
{
// Remove any existing DNSSEC records (NSEC, NSEC3, RRSIG)
SignUtils.removeGeneratedRecords(zonename, records);
// Sort the zone
Collections.sort(records, new RecordComparator());
// Remove duplicate records
SignUtils.removeDuplicateRecords(records);
// Generate DS records
SignUtils.generateDSRecords(zonename, records);
// Generate NSEC3 records
if (useOptIn)
{
SignUtils.generateOptInNSEC3Records(zonename,
records,
includedNames,
salt,
iterations);
}
else
{
SignUtils.generateNSEC3Records(zonename, records, salt, iterations);
}
// Re-sort so we can assemble into rrsets.
Collections.sort(records, new RecordComparator());
// Assemble into RRsets and sign.
RRset rrset = new RRset();
ArrayList signed_records = new ArrayList();
Name last_cut = null;
for (ListIterator i = records.listIterator(); i.hasNext();)
{
Record r = (Record) i.next();
// First record
if (rrset.size() == 0)
{
rrset.addRR(r);
continue;
@ -823,17 +909,34 @@ public class SignZone
JCEDnsSecSigner signer = new JCEDnsSecSigner();
// Sign the zone.
List signed_records = signZone(signer,
zonename,
records,
kskpairs,
keypairs,
state.start,
state.expire,
state.useOptIn,
state.optInConserve,
state.fullySignKeyset,
state.includeNames);
List signed_records;
if (state.useNsec3)
{
signed_records = signZoneNSEC3(signer,
zonename,
records,
kskpairs,
keypairs,
state.start,
state.expire,
state.fullySignKeyset,
state.useOptIn,
state.includeNames,
state.salt,
state.iterations);
}
else
{
signed_records = signZone(signer,
zonename,
records,
kskpairs,
keypairs,
state.start,
state.expire,
state.fullySignKeyset);
}
// write out the signed zone
// force multiline mode for now

View File

@ -0,0 +1,245 @@
/*
* $Id$
*
* Copyright (c) 2005 VeriSign. 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 com.verisignlabs.dnssec.security;
import org.xbill.DNS.*;
import org.xbill.DNS.utils.base16;
import org.xbill.DNS.utils.base32;
public class ProtoNSEC3
{
private boolean optInFlag;
private byte hashAlg;
private int iterations;
private byte[] salt;
private byte[] next;
private byte[] owner; // cached numerical owner value.
private TypeMap typemap;
private Name zone;
private Name name;
private int dclass;
private long ttl;
/**
* Creates an NSEC3 Record from the given data.
*
* @param next The following name in an ordered list of the zone
* @param types An array containing the types present.
*/
public ProtoNSEC3(byte[] owner, Name zone, int dclass, long ttl,
boolean optInFlag, byte hashAlg, int iterations, byte[] salt,
byte[] next, TypeMap typemap)
{
this.zone = zone;
this.owner = owner;
this.dclass = dclass;
this.ttl = ttl;
this.optInFlag = optInFlag;
this.hashAlg = hashAlg;
this.iterations = iterations;
this.salt = salt;
this.next = next;
this.typemap = typemap;
}
public ProtoNSEC3(byte[] owner, Name zone, int dclass, long ttl,
boolean optInFlag, byte hashAlg, int iterations, byte[] salt,
byte[] next, int[] types)
{
this(owner, zone, dclass, ttl, optInFlag, hashAlg, iterations, salt,
next, TypeMap.fromTypes(types));
}
private String hashToString(byte[] hash)
{
if (hash == null) return null;
return base32.toString(hash).toLowerCase();
}
public Name getName()
{
if (name == null)
{
try
{
name = new Name(hashToString(owner), zone);
}
catch (TextParseException e)
{
// This isn't going to happen.
}
}
return name;
}
public byte[] getNext()
{
return next;
}
public void setNext(byte[] next)
{
this.next = next;
}
public boolean getOptInFlag()
{
return optInFlag;
}
public void setOptInFlag(boolean optInFlag)
{
this.optInFlag = optInFlag;
}
public long getTTL()
{
return ttl;
}
public void setTTL(long ttl)
{
this.ttl = ttl;
}
public TypeMap getTypemap()
{
return typemap;
}
public int[] getTypes()
{
return typemap.getTypes();
}
public void setTypemap(TypeMap typemap)
{
this.typemap = typemap;
}
public int getDClass()
{
return dclass;
}
public byte getHashAlgorithm()
{
return hashAlg;
}
public int getIterations()
{
return iterations;
}
public byte[] getOwner()
{
return owner;
}
public byte[] getSalt()
{
return salt;
}
public Name getZone()
{
return zone;
}
public NSEC3Record getNSEC3Record()
{
return new NSEC3Record(getName(), dclass, ttl, optInFlag, hashAlg,
iterations, salt, next, getTypes());
}
public void mergeTypes(TypeMap new_types)
{
int[] nt = new_types.getTypes();
for (int i = 0; i < nt.length; i++)
{
if (!typemap.get(nt[i])) typemap.set(nt[i]);
}
}
public int compareTo(ProtoNSEC3 o)
{
if (o == null) return 1;
byte[] o_owner = o.getOwner();
int len = owner.length < o_owner.length ? o_owner.length : owner.length;
for (int i = 0; i < len; i++)
{
int d = (owner[i] - o_owner[i]);
if (d != 0) return d;
}
return owner.length - o_owner.length;
}
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append(getName());
sb.append(' ');
sb.append(ttl);
sb.append(' ');
sb.append(DClass.string(dclass));
sb.append(" NSEC3 ");
sb.append(optInFlag ? '1' : '0');
sb.append(' ');
sb.append(hashAlg);
sb.append(' ');
sb.append(iterations);
sb.append(' ');
sb.append(salt == null ? "-" : base16.toString(salt));
sb.append(' ');
String nextstr = (next == null) ? "(null)" : base32.toString(next)
.toLowerCase();
sb.append(nextstr);
int[] types = getTypes();
for (int i = 0; i < types.length; i++)
{
sb.append(" ");
sb.append(Type.string(types[i]));
}
return sb.toString();
}
public static class Comparator implements java.util.Comparator
{
public int compare(Object o1, Object o2)
{
return ((ProtoNSEC3) o1).compareTo((ProtoNSEC3) o2);
}
}
}

View File

@ -31,7 +31,6 @@ import java.util.logging.Logger;
import org.xbill.DNS.*;
import org.xbill.DNS.utils.base64;
/**
* This class contains a bunch of utility methods that are generally useful in
* signing zones.
@ -43,15 +42,16 @@ import org.xbill.DNS.utils.base64;
public class SignUtils
{
private static final int ASN1_INT = 0x02;
private static final int ASN1_SEQ = 0x30;
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;
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;
private static final int[] ENT_NSEC3_TYPES = {Type.RRSIG, Type.NSEC3};
private static Logger log;
private static Logger log;
static
{
@ -78,9 +78,9 @@ public class SignUtils
public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
Date start, Date expire, long sig_ttl)
{
return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl,
rrset.getType(), key.getAlgorithm(), (int) rrset.getTTL(), expire,
start, key.getFootprint(), key.getName(), null);
return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl, rrset
.getType(), key.getAlgorithm(), (int) rrset.getTTL(), expire, start,
key.getFootprint(), key.getName(), null);
}
/**
@ -97,9 +97,9 @@ public class SignUtils
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);
return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl, rec
.getType(), key.getAlgorithm(), rec.getTTL(), expire, start, key
.getFootprint(), key.getName(), null);
}
/**
@ -229,10 +229,10 @@ public class SignUtils
*/
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);
return new RRSIGRecord(presig.getName(), presig.getDClass(), presig
.getTTL(), presig.getTypeCovered(), presig.getAlgorithm(), presig
.getOrigTTL(), presig.getExpire(), presig.getTimeSigned(), presig
.getFootprint(), presig.getSigner(), signature);
}
/**
@ -479,7 +479,8 @@ public class SignUtils
// Current record is part of the current RRset.
if (rrset.getName().equals(r.getName())
&& rrset.getDClass() == r.getDClass()
&& ((r.getType() == Type.RRSIG && rrset.getType() == ((RRSIGRecord) r).getTypeCovered()) || rrset.getType() == r.getType()))
&& ((r.getType() == Type.RRSIG && rrset.getType() == ((RRSIGRecord) r)
.getTypeCovered()) || rrset.getType() == r.getType()))
{
rrset.addRR(r);
continue;
@ -650,11 +651,281 @@ public class SignUtils
log.finer("Generated: " + nsec);
}
public static void generateNSEC3Records(Name zonename, List records,
byte[] salt, int iterations) throws NoSuchAlgorithmException
{
List proto_nsec3s = new ArrayList();
NodeInfo current_node = null;
NodeInfo last_node = null;
// For detecting glue.
Name last_cut = null;
for (Iterator i = records.iterator(); i.hasNext();)
{
Record r = (Record) i.next();
Name r_name = r.getName();
int r_type = r.getType();
// Classify this record so we know if we can skip it.
int r_sectype = recordSecType(zonename, r_name, r_type, last_cut);
// skip irrelevant records
if (r_sectype == RR_INVALID || r_sectype == RR_GLUE) continue;
// note our last delegation point so we can recognize glue.
if (r_sectype == RR_DELEGATION) last_cut = r_name;
// For the first iteration, we create our current node.
if (current_node == null)
{
current_node = new NodeInfo(r);
continue;
}
// If we are at the same name, we are on the same node.
if (r_name.equals(current_node.name))
{
current_node.addType(r_type);
continue;
}
// At this point, r represents the start of a new node.
// So we move current_node to last_node and generate a new current node.
// But first, we need to do something with the last node.
generateNSEC3ForNode(last_node,
zonename,
salt,
iterations,
false,
proto_nsec3s);
last_node = current_node;
current_node = new NodeInfo(r);
}
// process last two nodes.
generateNSEC3ForNode(last_node,
zonename,
salt,
iterations,
false,
proto_nsec3s);
generateNSEC3ForNode(current_node,
zonename,
salt,
iterations,
false,
proto_nsec3s);
List nsec3s = finishNSEC3s(proto_nsec3s);
records.addAll(nsec3s);
}
public static void generateOptInNSEC3Records(Name zonename, List records,
List includedNames, byte[] salt, int iterations)
throws NoSuchAlgorithmException
{
List proto_nsec3s = new ArrayList();
NodeInfo current_node = null;
NodeInfo last_node = null;
// For detecting glue.
Name last_cut = null;
HashSet includeSet = null;
if (includedNames != null)
{
includeSet = new HashSet(includedNames);
}
for (Iterator i = records.iterator(); i.hasNext();)
{
Record r = (Record) i.next();
Name r_name = r.getName();
int r_type = r.getType();
// Classify this record so we know if we can skip it.
int r_sectype = recordSecType(zonename, r_name, r_type, last_cut);
// skip irrelevant records
if (r_sectype == RR_INVALID || r_sectype == RR_GLUE) continue;
// note our last delegation point so we can recognize glue.
if (r_sectype == RR_DELEGATION) last_cut = r_name;
// For the first iteration, we create our current node.
if (current_node == null)
{
current_node = new NodeInfo(r);
continue;
}
// If we are at the same name, we are on the same node.
if (r_name.equals(current_node.name))
{
current_node.addType(r_type);
continue;
}
if (includeSet != null && includeSet.contains(current_node.name))
{
current_node.isSecureNode = true;
}
// At this point, r represents the start of a new node.
// So we move current_node to last_node and generate a new current node.
// But first, we need to do something with the last node.
generateNSEC3ForNode(last_node,
zonename,
salt,
iterations,
true,
proto_nsec3s);
if (current_node.isSecureNode)
{
last_node = current_node;
}
else
{
last_node.hasOptInSpan = true;
}
current_node = new NodeInfo(r);
}
// process last two nodes.
generateNSEC3ForNode(last_node,
zonename,
salt,
iterations,
true,
proto_nsec3s);
generateNSEC3ForNode(current_node,
zonename,
salt,
iterations,
true,
proto_nsec3s);
List nsec3s = finishNSEC3s(proto_nsec3s);
records.addAll(nsec3s);
}
private static void generateNSEC3ForNode(NodeInfo node, Name zonename,
byte[] salt, int iterations, boolean optIn, List nsec3s)
throws NoSuchAlgorithmException
{
if (node == null) return;
if (optIn && !node.isSecureNode) return;
// Add our default types.
node.addType(Type.RRSIG);
node.addType(Type.NSEC3);
// Check for ENTs -- note this will generate duplicate ENTs because it
// doesn't use any context.
int ldiff = node.name.labels() - zonename.labels();
for (int i = 1; i < ldiff; i++)
{
Name n = new Name(node.name, i);
log.info("Generating ENT NSEC3 for " + n);
ProtoNSEC3 nsec3 = generateNSEC3(n,
zonename,
node.ttl,
salt,
iterations,
optIn,
null);
nsec3s.add(nsec3);
}
ProtoNSEC3 nsec3 = generateNSEC3(node.name,
zonename,
node.ttl,
salt,
iterations,
optIn,
node.getTypes());
nsec3s.add(nsec3);
}
private static ProtoNSEC3 generateNSEC3(Name name, Name zonename, long ttl,
byte[] salt, int iterations, boolean optIn, int[] types)
throws NoSuchAlgorithmException
{
byte[] hash = NSEC3Record.hash(name,
NSEC3Record.SHA1_DIGEST_ID,
iterations,
salt);
if (types == null)
{
types = ENT_NSEC3_TYPES;
}
ProtoNSEC3 r = new ProtoNSEC3(hash, zonename, DClass.IN, ttl, optIn,
NSEC3Record.SHA1_DIGEST_ID, iterations, salt, null, types);
log.finer("Generated: " + r);
return r;
}
private static List finishNSEC3s(List nsec3s)
{
if (nsec3s == null) return null;
Collections.sort(nsec3s, new ProtoNSEC3.Comparator());
ProtoNSEC3 prev_nsec3 = null;
ProtoNSEC3 cur_nsec3 = null;
byte[] first_nsec3_hash = null;
for (ListIterator i = nsec3s.listIterator(); i.hasNext();)
{
cur_nsec3 = (ProtoNSEC3) i.next();
// log.fine("finishNSEC3s: processing " + cur_nsec3);
// check to see if cur is a duplicate (by name)
if (prev_nsec3 != null
&& Arrays.equals(prev_nsec3.getOwner(), cur_nsec3.getOwner()))
{
log.fine("found duplicate NSEC3 (by name) -- merging type maps: "
+ prev_nsec3.getTypemap() + " and " + cur_nsec3.getTypemap());
i.remove();
prev_nsec3.mergeTypes(cur_nsec3.getTypemap());
log.fine("merged type map: " + prev_nsec3.getTypemap());
continue;
}
byte[] next = cur_nsec3.getOwner();
if (prev_nsec3 == null)
{
prev_nsec3 = cur_nsec3;
first_nsec3_hash = next;
continue;
}
prev_nsec3.setNext(next);
prev_nsec3 = cur_nsec3;
}
// Handle last NSEC3.
cur_nsec3.setNext(first_nsec3_hash);
List res = new ArrayList(nsec3s.size());
for (Iterator i = nsec3s.iterator(); i.hasNext();)
{
ProtoNSEC3 p = (ProtoNSEC3) i.next();
res.add(p.getNSEC3Record());
}
return res;
}
/**
* Given a canonical (by name) ordered list of records in a zone, generate
* the NXT records in place.
* the NSEC records in place.
*
* Note thatthe list that the records are stored in must support the
* Note that the list that the records are stored in must support the
* <code>listIterator.add</code> operation.
*
* @param zonename the name of the zone apex, used to distinguish between
@ -838,7 +1109,8 @@ public class SignUtils
{
Record r = (Record) i.next();
if (r.getType() == Type.RRSIG || r.getType() == Type.NSEC)
if (r.getType() == Type.RRSIG || r.getType() == Type.NSEC
|| r.getType() == Type.NSEC3)
{
i.remove();
}
@ -898,9 +1170,9 @@ public class SignUtils
byte[] digest = md.digest(os.toByteArray());
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl,
keyrec.getFootprint(), keyrec.getAlgorithm(),
DSRecord.SHA1_DIGEST_ID, digest);
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl, keyrec
.getFootprint(), keyrec.getAlgorithm(), DSRecord.SHA1_DIGEST_ID,
digest);
}
catch (NoSuchAlgorithmException e)