1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) 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 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 { 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 58 NSEC3Record() {} 59 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 */ 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 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 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 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 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 184 getHashAlgorithm() { 185 return hashAlg; 186 } 187 188 /** Returns the flags */ 189 public int 190 getFlags() { 191 return flags; 192 } 193 194 /** Returns the number of iterations */ 195 public int 196 getIterations() { 197 return iterations; 198 } 199 200 /** Returns the salt */ 201 public byte [] 202 getSalt() 203 { 204 return salt; 205 } 206 207 /** Returns the next hash */ 208 public byte [] 209 getNext() { 210 return next; 211 } 212 213 /** Returns the set of types defined for this name */ 214 public int [] 215 getTypes() { 216 return types.toArray(); 217 } 218 219 /** Returns whether a specific type is in the set of types. */ 220 public boolean 221 hasType(int type) 222 { 223 return types.contains(type); 224 } 225 226 static byte [] 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 [] 261 hashName(Name name) throws NoSuchAlgorithmException 262 { 263 return hashName(name, hashAlg, iterations, salt); 264 } 265 266 } 267