Class: Dnsruby::RR::NSEC3

Inherits:
Dnsruby::RR show all
Defined in:
lib/Dnsruby/resource/NSEC3.rb

Overview

The NSEC3 Resource Record (RR) provides authenticated denial of existence for DNS Resource Record Sets.

The NSEC3 RR lists RR types present at the original owner name of the NSEC3 RR. It includes the next hashed owner name in the hash order of the zone. The complete set of NSEC3 RRs in a zone indicates which RRSets exist for the original owner name of the RR and form a chain of hashed owner names in the zone. This information is used to provide authenticated denial of existence for DNS data. To provide protection against zone enumeration, the owner names used in the NSEC3 RR are cryptographic hashes of the original owner name prepended as a single label to the name of the zone. The NSEC3 RR indicates which hash function is used to construct the hash, which salt is used, and how many iterations of the hash function are performed over the original owner name.

Constant Summary collapse

ClassValue =

:nodoc: all

nil
TypeValue =

:nodoc: all

Types::NSEC3
OPT_OUT =
1

Constants inherited from Dnsruby::RR

ClassInsensitiveTypes

Instance Attribute Summary collapse

Attributes inherited from Dnsruby::RR

#klass, #name, #rdata, #ttl, #type

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Dnsruby::RR

#<=>, #==, #clone, create, #eql?, find_class, #from_hash, get_class, get_num, #hash, implemented_rrs, #init_defaults, new_from_data, new_from_hash, new_from_string, #rdlength, #sameRRset, #to_s

Instance Attribute Details

#flagsObject

The Flags field contains 8 one-bit flags that can be used to indicate different processing. All undefined flags must be zero. The only flag defined by the NSEC3 specification is the Opt-Out flag.



70
71
72
# File 'lib/Dnsruby/resource/NSEC3.rb', line 70

def flags
  @flags
end

#hash_algObject

The Hash Algorithm field identifies the cryptographic hash algorithm used to construct the hash-value.



66
67
68
# File 'lib/Dnsruby/resource/NSEC3.rb', line 66

def hash_alg
  @hash_alg
end

#hash_lengthObject

The Hash Length field defines the length of the Next Hashed Owner Name field, ranging in value from 1 to 255 octets.



79
80
81
# File 'lib/Dnsruby/resource/NSEC3.rb', line 79

def hash_length
  @hash_length
end

#iterationsObject

The Iterations field defines the number of additional times the hash function has been performed.



73
74
75
# File 'lib/Dnsruby/resource/NSEC3.rb', line 73

def iterations
  @iterations
end

#next_hashedObject

The Next Hashed Owner Name field contains the next hashed owner name in hash order.



82
83
84
# File 'lib/Dnsruby/resource/NSEC3.rb', line 82

def next_hashed
  @next_hashed
end

#salt_lengthObject (readonly)

The Salt Length field defines the length of the Salt field in octets, ranging in value from 0 to 255.



76
77
78
# File 'lib/Dnsruby/resource/NSEC3.rb', line 76

def salt_length
  @salt_length
end

#typesObject

The Type Bit Maps field identifies the RRset types that exist at the NSEC RR’s owner name



85
86
87
# File 'lib/Dnsruby/resource/NSEC3.rb', line 85

def types
  @types
end

Class Method Details

.calculate_hash(name, iterations, salt, hash_alg) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/Dnsruby/resource/NSEC3.rb', line 101

def NSEC3.calculate_hash(name, iterations, salt, hash_alg)
  # RFC5155
  #5.  Calculation of the Hash

  #   Define H(x) to be the hash of x using the Hash Algorithm selected by
  #   the NSEC3 RR, k to be the number of Iterations, and || to indicate
  #   concatenation.  Then define:
  #
  #      IH(salt, x, 0) = H(x || salt), and
  #
  #      IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0
  #
  #   Then the calculated hash of an owner name is
  #
  #      IH(salt, owner name, iterations),
  #
  #   where the owner name is in the canonical form, defined as:
  #
  #   The wire format of the owner name where:
  #
  #   1.  The owner name is fully expanded (no DNS name compression) and
  #       fully qualified;
  #   2.  All uppercase US-ASCII letters are replaced by the corresponding
  #       lowercase US-ASCII letters;
  #   3.  If the owner name is a wildcard name, the owner name is in its
  #       original unexpanded form, including the "*" label (no wildcard
  #       substitution);
  #
  #   This form is as defined in Section 6.2 of [RFC 4034].
  #

  n = Name.create(name)
  out = n.canonical
  begin
  (0..iterations).each  {
    out =NSEC3.h(out + salt, hash_alg);
  }
  return Base32.encode32hex(out).downcase
  rescue ArgumentError
  TheLog.error("Unknown hash algorithm #{hash_alg} used for NSEC3 hash")
  return "Unknown NSEC3 hash algorithm"
  end
end

.decode_next_hashed(input) ⇒ Object



253
254
255
# File 'lib/Dnsruby/resource/NSEC3.rb', line 253

def NSEC3.decode_next_hashed(input)
  return Base32.decode32hex(input)
end

.decode_rdata(msg) ⇒ Object

:nodoc: all



325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/Dnsruby/resource/NSEC3.rb', line 325

def self.decode_rdata(msg) #:nodoc: all
  hash_alg, flags, iterations, salt_length = msg.get_unpack("ccnc")
  # Salt may be omitted
  salt = []
  if (salt_length > 0)
    salt = msg.get_bytes(salt_length)
  end
  hash_length, = msg.get_unpack("c")
  next_hashed = msg.get_bytes(hash_length)
  types = NSEC.decode_types(msg.get_bytes)
  return self.new(
    [hash_alg, flags, iterations, salt_length, salt, hash_length, next_hashed, types])
end

.decode_salt(input) ⇒ Object



235
236
237
238
239
240
# File 'lib/Dnsruby/resource/NSEC3.rb', line 235

def NSEC3.decode_salt(input)
  if (input == "-")
    return ""
  end
  return [input].pack("H*")
end

.encode_next_hashed(n) ⇒ Object



261
262
263
# File 'lib/Dnsruby/resource/NSEC3.rb', line 261

def NSEC3.encode_next_hashed(n)
  return Base32.encode32hex(n).downcase
end

.encode_salt(s) ⇒ Object



242
243
244
245
246
247
# File 'lib/Dnsruby/resource/NSEC3.rb', line 242

def NSEC3.encode_salt(s)
  if (!s || s.length == 0)
    return "-"
  end
  return s.unpack("H*")[0]
end

.h(x, hash_alg) ⇒ Object

:nodoc: all

Raises:

  • (ArgumentError)


149
150
151
152
153
154
# File 'lib/Dnsruby/resource/NSEC3.rb', line 149

def NSEC3.h(x, hash_alg) # :nodoc: all
  if (Nsec3HashAlgorithms.SHA_1 == hash_alg)
    return Digest::SHA1.digest(x)
  end
  raise ArgumentError.new("Unknown hash algorithm")
end

Instance Method Details

#add_type(t) ⇒ Object



178
179
180
# File 'lib/Dnsruby/resource/NSEC3.rb', line 178

def add_type(t)
  self.types=(@types + [t])
end

#calculate_hashObject



97
98
99
# File 'lib/Dnsruby/resource/NSEC3.rb', line 97

def calculate_hash
  return NSEC3.calculate_hash(@name, @iterations, @salt, @hash_alg)
end

#check_name_in_range(name) ⇒ Object



87
88
89
90
# File 'lib/Dnsruby/resource/NSEC3.rb', line 87

def check_name_in_range(name)
  # @TODO@ Check if the name is covered by this record
  return false
end

#check_name_in_wildcard_range(name) ⇒ Object



92
93
94
95
# File 'lib/Dnsruby/resource/NSEC3.rb', line 92

def check_name_in_wildcard_range(name)
  # @TODO@ Check if the name is covered by this record
  return false
end

#decode_next_hashed(input) ⇒ Object



249
250
251
# File 'lib/Dnsruby/resource/NSEC3.rb', line 249

def decode_next_hashed(input)
@next_hashed = NSEC3.decode_next_hashed(input)
end

#encode_next_hashed(n) ⇒ Object



257
258
259
# File 'lib/Dnsruby/resource/NSEC3.rb', line 257

def encode_next_hashed(n)
  return NSEC3.encode_next_hashed(n)
end

#encode_rdata(msg, canonical = false) ⇒ Object

:nodoc: all



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/Dnsruby/resource/NSEC3.rb', line 308

def encode_rdata(msg, canonical=false) #:nodoc: all
#        s = salt()
  s = @salt
  sl = s.length()
  if (s == "-")
    sl = 0
  end
  msg.put_pack("ccnc", @hash_alg.code, @flags, @iterations, sl)
  if (sl > 0)
    msg.put_bytes(s)
  end
  msg.put_pack("c", @hash_length)
  msg.put_bytes(@next_hashed)
  types = NSEC.encode_types(self)
  msg.put_bytes(types)
end

#from_data(data) ⇒ Object

:nodoc: all



211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/Dnsruby/resource/NSEC3.rb', line 211

def from_data(data) #:nodoc: all
  hash_alg, flags, iterations, salt_length, salt, hash_length, next_hashed, types = data
  self.hash_alg=(hash_alg)
  self.flags=(flags)
  self.iterations=(iterations)
#        self.salt_length=(salt_length)
#        self.salt=(salt)
  @salt=salt
  self.hash_length=(hash_length)
  self.next_hashed=(next_hashed)
  self.types=(types)
end

#from_string(input) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/Dnsruby/resource/NSEC3.rb', line 265

def from_string(input)
  if (input.length > 0)
    data = input.split
    self.hash_alg=(data[0]).to_i
    self.flags=(data[1]).to_i
    self.iterations=(data[2]).to_i
    self.salt=(data[3])

    len = data[0].length + data[1].length + data[2].length + data[3].length + 4
    # There may or may not be brackets around next_hashed
    if (data[4] == "(")
      len = len + data[4].length + 1
    end
    next_hashed_and_types = (input[len, input.length-len])
    data2 = next_hashed_and_types.split()


    self.next_hashed=decode_next_hashed(data2[0])
    self.hash_length=(@next_hashed.length)
    len2 = data2[0].length + 1
    self.types = next_hashed_and_types[len2, next_hashed_and_types.length - len2]
    #          self.types=data2[1]
    #          #          len = data[0].length + data[1].length + data[2].length + data[3].length + data[5].length + 7
    #          #          self.types=(input[len, input.length-len])
  end
end

#h(x) ⇒ Object

:nodoc: all



145
146
147
# File 'lib/Dnsruby/resource/NSEC3.rb', line 145

def h(x) # :nodoc: all
  return NSEC3.h(x, @hash_alg)
end

#opt_out?Boolean

If the Opt-Out flag is set, the NSEC3 record covers zero or more unsigned delegations.

Returns:

  • (Boolean)


193
194
195
# File 'lib/Dnsruby/resource/NSEC3.rb', line 193

def opt_out?
  return (@flags==OPT_OUT)
end

#rdata_to_stringObject

:nodoc: all



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/Dnsruby/resource/NSEC3.rb', line 292

def rdata_to_string #:nodoc: all
  if (@next_hashed!=nil)
    type_strings = []
    @types.each do |t|
      type_strings.push(t.string)
    end
    #          salt = NSEC3.encode_salt(@salt)
    salt = salt()
    next_hashed = encode_next_hashed(@next_hashed)
    types = type_strings.join(" ")
    return "#{@hash_alg.code} #{@flags} #{@iterations} #{salt} ( #{next_hashed} #{types} )"
  else
    return ""
  end
end

#saltObject

The Salt field is appended to the original owner name before hashing in order to defend against pre-calculated dictionary attacks.



226
227
228
# File 'lib/Dnsruby/resource/NSEC3.rb', line 226

def salt
  return NSEC3.encode_salt(@salt)
end

#salt=(s) ⇒ Object



230
231
232
233
# File 'lib/Dnsruby/resource/NSEC3.rb', line 230

def salt=(s)
  @salt = NSEC3.decode_salt(s)
  @salt_length = @salt.length
end