1 // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3 package org.xbill.DNS;
4
5 import java.io.*;
6 import java.security.*;
7
8 import 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
22 public class NSEC3Record extends Record {
23
24 public static class Flags {
25 /**
26 * NSEC3 flags identifiers.
27 */
28
Flags()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
37 public static class Digest {
Digest()38 private Digest() {}
39
40 /** SHA-1 */
41 public static final int SHA1 = 1;
42 }
43
44 public static final int SHA1_DIGEST_ID = Digest.SHA1;
45
46 private static final long serialVersionUID = -7123504635968932855L;
47
48 private int hashAlg;
49 private int flags;
50 private int iterations;
51 private byte [] salt;
52 private byte [] next;
53 private TypeBitmap types;
54
55 private static final base32 b32 = new base32(base32.Alphabet.BASE32HEX,
56 false, false);
57
NSEC3Record()58 NSEC3Record() {}
59
getObject()60 Record 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 */
NSEC3Record(Name name, int dclass, long ttl, int hashAlg, int flags, int iterations, byte [] salt, byte [] next, int [] types)77 public 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
103 void
rrFromWire(DNSInput in)104 rrFromWire(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
120 void
rrToWire(DNSOutput out, Compression c, boolean canonical)121 rrToWire(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
137 void
rdataFromString(Tokenizer st, Name origin)138 rdataFromString(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 */
158 String
rrToString()159 rrToString() {
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 */
183 public int
getHashAlgorithm()184 getHashAlgorithm() {
185 return hashAlg;
186 }
187
188 /** Returns the flags */
189 public int
getFlags()190 getFlags() {
191 return flags;
192 }
193
194 /** Returns the number of iterations */
195 public int
getIterations()196 getIterations() {
197 return iterations;
198 }
199
200 /** Returns the salt */
201 public byte []
getSalt()202 getSalt()
203 {
204 return salt;
205 }
206
207 /** Returns the next hash */
208 public byte []
getNext()209 getNext() {
210 return next;
211 }
212
213 /** Returns the set of types defined for this name */
214 public int []
getTypes()215 getTypes() {
216 return types.toArray();
217 }
218
219 /** Returns whether a specific type is in the set of types. */
220 public boolean
hasType(int type)221 hasType(int type)
222 {
223 return types.contains(type);
224 }
225
226 static byte []
hashName(Name name, int hashAlg, int iterations, byte [] salt)227 hashName(Name name, int hashAlg, int iterations, byte [] salt)
228 throws 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 */
260 public byte []
hashName(Name name)261 hashName(Name name) throws NoSuchAlgorithmException
262 {
263 return hashName(name, hashAlg, iterations, salt);
264 }
265
266 }
267