// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package org.xbill.DNS;
import java.io.*;
import java.security.*;
import org.xbill.DNS.utils.*;
/**
* Next SECure name 3 - this record contains the next hashed name in an
* ordered list of hashed names in the zone, and a set of types for which
* records exist for this name. The presence of this record in a response
* signifies a negative response from a DNSSEC-signed zone.
*
* This replaces the NSEC and NXT records, when used.
*
* @author Brian Wellington
* @author David Blacka
*/
public class NSEC3Record extends Record {
public static class Flags {
/**
* NSEC3 flags identifiers.
*/
private Flags() {}
/** Unsigned delegation are not included in the NSEC3 chain.
*
*/
public static final int OPT_OUT = 0x01;
}
public static class Digest {
private Digest() {}
/** SHA-1 */
public static final int SHA1 = 1;
}
public static final int SHA1_DIGEST_ID = Digest.SHA1;
private static final long serialVersionUID = -7123504635968932855L;
private int hashAlg;
private int flags;
private int iterations;
private byte [] salt;
private byte [] next;
private TypeBitmap types;
private static final base32 b32 = new base32(base32.Alphabet.BASE32HEX,
false, false);
NSEC3Record() {}
Record getObject() {
return new NSEC3Record();
}
/**
* Creates an NSEC3 record from the given data.
*
* @param name The ownername of the NSEC3 record (base32'd hash plus zonename).
* @param dclass The class.
* @param ttl The TTL.
* @param hashAlg The hash algorithm.
* @param flags The value of the flags field.
* @param iterations The number of hash iterations.
* @param salt The salt to use (may be null).
* @param next The next hash (may not be null).
* @param types The types present at the original ownername.
*/
public NSEC3Record(Name name, int dclass, long ttl, int hashAlg,
int flags, int iterations, byte [] salt, byte [] next,
int [] types)
{
super(name, Type.NSEC3, dclass, ttl);
this.hashAlg = checkU8("hashAlg", hashAlg);
this.flags = checkU8("flags", flags);
this.iterations = checkU16("iterations", iterations);
if (salt != null) {
if (salt.length > 255)
throw new IllegalArgumentException("Invalid salt");
if (salt.length > 0) {
this.salt = new byte[salt.length];
System.arraycopy(salt, 0, this.salt, 0, salt.length);
}
}
if (next.length > 255) {
throw new IllegalArgumentException("Invalid next hash");
}
this.next = new byte[next.length];
System.arraycopy(next, 0, this.next, 0, next.length);
this.types = new TypeBitmap(types);
}
void
rrFromWire(DNSInput in) throws IOException {
hashAlg = in.readU8();
flags = in.readU8();
iterations = in.readU16();
int salt_length = in.readU8();
if (salt_length > 0)
salt = in.readByteArray(salt_length);
else
salt = null;
int next_length = in.readU8();
next = in.readByteArray(next_length);
types = new TypeBitmap(in);
}
void
rrToWire(DNSOutput out, Compression c, boolean canonical) {
out.writeU8(hashAlg);
out.writeU8(flags);
out.writeU16(iterations);
if (salt != null) {
out.writeU8(salt.length);
out.writeByteArray(salt);
} else
out.writeU8(0);
out.writeU8(next.length);
out.writeByteArray(next);
types.toWire(out);
}
void
rdataFromString(Tokenizer st, Name origin) throws IOException {
hashAlg = st.getUInt8();
flags = st.getUInt8();
iterations = st.getUInt16();
String s = st.getString();
if (s.equals("-"))
salt = null;
else {
st.unget();
salt = st.getHexString();
if (salt.length > 255)
throw st.exception("salt value too long");
}
next = st.getBase32String(b32);
types = new TypeBitmap(st);
}
/** Converts rdata to a String */
String
rrToString() {
StringBuffer sb = new StringBuffer();
sb.append(hashAlg);
sb.append(' ');
sb.append(flags);
sb.append(' ');
sb.append(iterations);
sb.append(' ');
if (salt == null)
sb.append('-');
else
sb.append(base16.toString(salt));
sb.append(' ');
sb.append(b32.toString(next));
if (!types.empty()) {
sb.append(' ');
sb.append(types.toString());
}
return sb.toString();
}
/** Returns the hash algorithm */
public int
getHashAlgorithm() {
return hashAlg;
}
/** Returns the flags */
public int
getFlags() {
return flags;
}
/** Returns the number of iterations */
public int
getIterations() {
return iterations;
}
/** Returns the salt */
public byte []
getSalt()
{
return salt;
}
/** Returns the next hash */
public byte []
getNext() {
return next;
}
/** Returns the set of types defined for this name */
public int []
getTypes() {
return types.toArray();
}
/** Returns whether a specific type is in the set of types. */
public boolean
hasType(int type)
{
return types.contains(type);
}
static byte []
hashName(Name name, int hashAlg, int iterations, byte [] salt)
throws NoSuchAlgorithmException
{
MessageDigest digest;
switch (hashAlg) {
case Digest.SHA1:
digest = MessageDigest.getInstance("sha-1");
break;
default:
throw new NoSuchAlgorithmException("Unknown NSEC3 algorithm" +
"identifier: " +
hashAlg);
}
byte [] hash = null;
for (int i = 0; i <= iterations; i++) {
digest.reset();
if (i == 0)
digest.update(name.toWireCanonical());
else
digest.update(hash);
if (salt != null)
digest.update(salt);
hash = digest.digest();
}
return hash;
}
/**
* Hashes a name with the parameters of this NSEC3 record.
* @param name The name to hash
* @return The hashed version of the name
* @throws NoSuchAlgorithmException The hash algorithm is unknown.
*/
public byte []
hashName(Name name) throws NoSuchAlgorithmException
{
return hashName(name, hashAlg, iterations, salt);
}
}