1// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.*;
6import java.security.*;
7
8import org.xbill.DNS.utils.*;
9
10/**
11 * Next SECure name 3 - this record contains the next hashed name in an
12 * ordered list of hashed names in the zone, and a set of types for which
13 * records exist for this name. The presence of this record in a response
14 * signifies a negative response from a DNSSEC-signed zone.
15 *
16 * This replaces the NSEC and NXT records, when used.
17 *
18 * @author Brian Wellington
19 * @author David Blacka
20 */
21
22public class NSEC3Record extends Record {
23
24public static class Flags {
25	/**
26	 * NSEC3 flags identifiers.
27	 */
28
29	private Flags() {}
30
31	/** Unsigned delegation are not included in the NSEC3 chain.
32	 *
33	 */
34	public static final int OPT_OUT = 0x01;
35}
36
37public static class Digest {
38	private Digest() {}
39
40	/** SHA-1 */
41	public static final int SHA1 = 1;
42}
43
44public static final int SHA1_DIGEST_ID = Digest.SHA1;
45
46private static final long serialVersionUID = -7123504635968932855L;
47
48private int hashAlg;
49private int flags;
50private int iterations;
51private byte [] salt;
52private byte [] next;
53private TypeBitmap types;
54
55private static final base32 b32 = new base32(base32.Alphabet.BASE32HEX,
56					     false, false);
57
58NSEC3Record() {}
59
60Record getObject() {
61	return new NSEC3Record();
62}
63
64/**
65 * Creates an NSEC3 record from the given data.
66 *
67 * @param name The ownername of the NSEC3 record (base32'd hash plus zonename).
68 * @param dclass The class.
69 * @param ttl The TTL.
70 * @param hashAlg The hash algorithm.
71 * @param flags The value of the flags field.
72 * @param iterations The number of hash iterations.
73 * @param salt The salt to use (may be null).
74 * @param next The next hash (may not be null).
75 * @param types The types present at the original ownername.
76 */
77public NSEC3Record(Name name, int dclass, long ttl, int hashAlg,
78		   int flags, int iterations, byte [] salt, byte [] next,
79		   int [] types)
80{
81	super(name, Type.NSEC3, dclass, ttl);
82	this.hashAlg = checkU8("hashAlg", hashAlg);
83	this.flags = checkU8("flags", flags);
84	this.iterations = checkU16("iterations", iterations);
85
86	if (salt != null) {
87		if (salt.length > 255)
88			throw new IllegalArgumentException("Invalid salt");
89		if (salt.length > 0) {
90			this.salt = new byte[salt.length];
91			System.arraycopy(salt, 0, this.salt, 0, salt.length);
92		}
93	}
94
95	if (next.length > 255) {
96		throw new IllegalArgumentException("Invalid next hash");
97	}
98	this.next = new byte[next.length];
99	System.arraycopy(next, 0, this.next, 0, next.length);
100	this.types = new TypeBitmap(types);
101}
102
103void
104rrFromWire(DNSInput in) throws IOException {
105	hashAlg = in.readU8();
106	flags = in.readU8();
107	iterations = in.readU16();
108
109	int salt_length = in.readU8();
110	if (salt_length > 0)
111		salt = in.readByteArray(salt_length);
112	else
113		salt = null;
114
115	int next_length = in.readU8();
116	next = in.readByteArray(next_length);
117	types = new TypeBitmap(in);
118}
119
120void
121rrToWire(DNSOutput out, Compression c, boolean canonical) {
122	out.writeU8(hashAlg);
123	out.writeU8(flags);
124	out.writeU16(iterations);
125
126	if (salt != null) {
127		out.writeU8(salt.length);
128		out.writeByteArray(salt);
129	} else
130		out.writeU8(0);
131
132	out.writeU8(next.length);
133	out.writeByteArray(next);
134	types.toWire(out);
135}
136
137void
138rdataFromString(Tokenizer st, Name origin) throws IOException {
139	hashAlg = st.getUInt8();
140	flags = st.getUInt8();
141	iterations = st.getUInt16();
142
143	String s = st.getString();
144	if (s.equals("-"))
145		salt = null;
146	else {
147		st.unget();
148		salt = st.getHexString();
149		if (salt.length > 255)
150			throw st.exception("salt value too long");
151	}
152
153	next = st.getBase32String(b32);
154	types = new TypeBitmap(st);
155}
156
157/** Converts rdata to a String */
158String
159rrToString() {
160	StringBuffer sb = new StringBuffer();
161	sb.append(hashAlg);
162	sb.append(' ');
163	sb.append(flags);
164	sb.append(' ');
165	sb.append(iterations);
166	sb.append(' ');
167	if (salt == null)
168		sb.append('-');
169	else
170		sb.append(base16.toString(salt));
171	sb.append(' ');
172	sb.append(b32.toString(next));
173
174	if (!types.empty()) {
175		sb.append(' ');
176		sb.append(types.toString());
177	}
178
179	return sb.toString();
180}
181
182/** Returns the hash algorithm */
183public int
184getHashAlgorithm() {
185	return hashAlg;
186}
187
188/** Returns the flags */
189public int
190getFlags() {
191	return flags;
192}
193
194/** Returns the number of iterations */
195public int
196getIterations() {
197	return iterations;
198}
199
200/** Returns the salt */
201public byte []
202getSalt()
203{
204	return salt;
205}
206
207/** Returns the next hash */
208public byte []
209getNext() {
210	return next;
211}
212
213  /** Returns the set of types defined for this name */
214public int []
215getTypes() {
216	return types.toArray();
217}
218
219/** Returns whether a specific type is in the set of types. */
220public boolean
221hasType(int type)
222{
223	return types.contains(type);
224}
225
226static byte []
227hashName(Name name, int hashAlg, int iterations, byte [] salt)
228throws NoSuchAlgorithmException
229{
230	MessageDigest digest;
231	switch (hashAlg) {
232	case Digest.SHA1:
233		digest = MessageDigest.getInstance("sha-1");
234		break;
235	default:
236		throw new NoSuchAlgorithmException("Unknown NSEC3 algorithm" +
237						   "identifier: " +
238						   hashAlg);
239	}
240	byte [] hash = null;
241	for (int i = 0; i <= iterations; i++) {
242		digest.reset();
243		if (i == 0)
244			digest.update(name.toWireCanonical());
245		else
246			digest.update(hash);
247		if (salt != null)
248			digest.update(salt);
249		hash = digest.digest();
250	}
251	return hash;
252}
253
254/**
255 * Hashes a name with the parameters of this NSEC3 record.
256 * @param name The name to hash
257 * @return The hashed version of the name
258 * @throws NoSuchAlgorithmException The hash algorithm is unknown.
259 */
260public byte []
261hashName(Name name) throws NoSuchAlgorithmException
262{
263	return hashName(name, hashAlg, iterations, salt);
264}
265
266}
267