hickory_proto/rr/dnssec/rdata/nsec3.rs
1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! NSEC record types
9
10use std::fmt;
11
12#[cfg(feature = "serde-config")]
13use serde::{Deserialize, Serialize};
14
15use crate::{
16 error::*,
17 rr::{
18 dnssec::Nsec3HashAlgorithm, type_bit_map::*, RData, RecordData, RecordDataDecodable,
19 RecordType,
20 },
21 serialize::binary::*,
22};
23
24use super::DNSSECRData;
25
26/// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3), NSEC3, March 2008
27///
28/// ```text
29/// 3. The NSEC3 Resource Record
30///
31/// The NSEC3 Resource Record (RR) provides authenticated denial of
32/// existence for DNS Resource Record Sets.
33///
34/// The NSEC3 RR lists RR types present at the original owner name of the
35/// NSEC3 RR. It includes the next hashed owner name in the hash order
36/// of the zone. The complete set of NSEC3 RRs in a zone indicates which
37/// RRSets exist for the original owner name of the RR and form a chain
38/// of hashed owner names in the zone. This information is used to
39/// provide authenticated denial of existence for DNS data. To provide
40/// protection against zone enumeration, the owner names used in the
41/// NSEC3 RR are cryptographic hashes of the original owner name
42/// prepended as a single label to the name of the zone. The NSEC3 RR
43/// indicates which hash function is used to construct the hash, which
44/// salt is used, and how many iterations of the hash function are
45/// performed over the original owner name. The hashing technique is
46/// described fully in Section 5.
47///
48/// Hashed owner names of unsigned delegations may be excluded from the
49/// chain. An NSEC3 RR whose span covers the hash of an owner name or
50/// "next closer" name of an unsigned delegation is referred to as an
51/// Opt-Out NSEC3 RR and is indicated by the presence of a flag.
52///
53/// The owner name for the NSEC3 RR is the base32 encoding of the hashed
54/// owner name prepended as a single label to the name of the zone.
55///
56/// The type value for the NSEC3 RR is 50.
57///
58/// The NSEC3 RR RDATA format is class independent and is described
59/// below.
60///
61/// The class MUST be the same as the class of the original owner name.
62///
63/// The NSEC3 RR SHOULD have the same TTL value as the SOA minimum TTL
64/// field. This is in the spirit of negative caching [RFC2308].
65///
66/// 3.2. NSEC3 RDATA Wire Format
67///
68/// The RDATA of the NSEC3 RR is as shown below:
69///
70/// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
71/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
72/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73/// | Hash Alg. | Flags | Iterations |
74/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
75/// | Salt Length | Salt /
76/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77/// | Hash Length | Next Hashed Owner Name /
78/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79/// / Type Bit Maps /
80/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81///
82/// Hash Algorithm is a single octet.
83///
84/// Flags field is a single octet, the Opt-Out flag is the least
85/// significant bit, as shown below:
86///
87/// 0 1 2 3 4 5 6 7
88/// +-+-+-+-+-+-+-+-+
89/// | |O|
90/// +-+-+-+-+-+-+-+-+
91///
92/// Iterations is represented as a 16-bit unsigned integer, with the most
93/// significant bit first.
94///
95/// Salt Length is represented as an unsigned octet. Salt Length
96/// represents the length of the Salt field in octets. If the value is
97/// zero, the following Salt field is omitted.
98///
99/// Salt, if present, is encoded as a sequence of binary octets. The
100/// length of this field is determined by the preceding Salt Length
101/// field.
102///
103/// Hash Length is represented as an unsigned octet. Hash Length
104/// represents the length of the Next Hashed Owner Name field in octets.
105///
106/// The next hashed owner name is not base32 encoded, unlike the owner
107/// name of the NSEC3 RR. It is the unmodified binary hash value. It
108/// does not include the name of the containing zone. The length of this
109/// field is determined by the preceding Hash Length field.
110/// ```
111#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
112#[derive(Debug, PartialEq, Eq, Hash, Clone)]
113pub struct NSEC3 {
114 hash_algorithm: Nsec3HashAlgorithm,
115 opt_out: bool,
116 iterations: u16,
117 salt: Vec<u8>,
118 next_hashed_owner_name: Vec<u8>,
119 type_bit_maps: Vec<RecordType>,
120}
121
122impl NSEC3 {
123 /// Constructs a new NSEC3 record
124 pub fn new(
125 hash_algorithm: Nsec3HashAlgorithm,
126 opt_out: bool,
127 iterations: u16,
128 salt: Vec<u8>,
129 next_hashed_owner_name: Vec<u8>,
130 type_bit_maps: Vec<RecordType>,
131 ) -> Self {
132 Self {
133 hash_algorithm,
134 opt_out,
135 iterations,
136 salt,
137 next_hashed_owner_name,
138 type_bit_maps,
139 }
140 }
141
142 /// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3.1.1), NSEC3, March 2008
143 ///
144 /// ```text
145 /// 3.1.1. Hash Algorithm
146 ///
147 /// The Hash Algorithm field identifies the cryptographic hash algorithm
148 /// used to construct the hash-value.
149 ///
150 /// The values for this field are defined in the NSEC3 hash algorithm
151 /// registry defined in Section 11.
152 /// ```
153 pub fn hash_algorithm(&self) -> Nsec3HashAlgorithm {
154 self.hash_algorithm
155 }
156
157 /// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3.1.2), NSEC3, March 2008
158 ///
159 /// ```text
160 /// 3.1.2. Flags
161 ///
162 /// The Flags field contains 8 one-bit flags that can be used to indicate
163 /// different processing. All undefined flags must be zero. The only
164 /// flag defined by this specification is the Opt-Out flag.
165 ///
166 /// 3.1.2.1. Opt-Out Flag
167 ///
168 /// If the Opt-Out flag is set, the NSEC3 record covers zero or more
169 /// unsigned delegations.
170 ///
171 /// If the Opt-Out flag is clear, the NSEC3 record covers zero unsigned
172 /// delegations.
173 ///
174 /// The Opt-Out Flag indicates whether this NSEC3 RR may cover unsigned
175 /// delegations. It is the least significant bit in the Flags field.
176 /// See Section 6 for details about the use of this flag.
177 /// ```
178 pub fn opt_out(&self) -> bool {
179 self.opt_out
180 }
181
182 /// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3.1.3), NSEC3, March 2008
183 ///
184 /// ```text
185 /// 3.1.3. Iterations
186 ///
187 /// The Iterations field defines the number of additional times the hash
188 /// function has been performed. More iterations result in greater
189 /// resiliency of the hash value against dictionary attacks, but at a
190 /// higher computational cost for both the server and resolver. See
191 /// Section 5 for details of the use of this field, and Section 10.3 for
192 /// limitations on the value.
193 /// ```
194 pub fn iterations(&self) -> u16 {
195 self.iterations
196 }
197
198 /// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3.1.5), NSEC3, March 2008
199 ///
200 /// ```text
201 /// 3.1.5. Salt
202 ///
203 /// The Salt field is appended to the original owner name before hashing
204 /// in order to defend against pre-calculated dictionary attacks. See
205 /// Section 5 for details on how the salt is used.
206 /// ```
207 pub fn salt(&self) -> &[u8] {
208 &self.salt
209 }
210
211 /// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3.1.7), NSEC3, March 2008
212 ///
213 /// ```text
214 /// 3.1.7. Next Hashed Owner Name
215 ///
216 /// The Next Hashed Owner Name field contains the next hashed owner name
217 /// in hash order. This value is in binary format. Given the ordered
218 /// set of all hashed owner names, the Next Hashed Owner Name field
219 /// contains the hash of an owner name that immediately follows the owner
220 /// name of the given NSEC3 RR. The value of the Next Hashed Owner Name
221 /// field in the last NSEC3 RR in the zone is the same as the hashed
222 /// owner name of the first NSEC3 RR in the zone in hash order. Note
223 /// that, unlike the owner name of the NSEC3 RR, the value of this field
224 /// does not contain the appended zone name.
225 /// ```
226 pub fn next_hashed_owner_name(&self) -> &[u8] {
227 &self.next_hashed_owner_name
228 }
229
230 /// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3.1.8), NSEC3, March 2008
231 ///
232 /// ```text
233 /// 3.1.8. Type Bit Maps
234 ///
235 /// The Type Bit Maps field identifies the RRSet types that exist at the
236 /// original owner name of the NSEC3 RR.
237 /// ```
238 pub fn type_bit_maps(&self) -> &[RecordType] {
239 &self.type_bit_maps
240 }
241
242 /// Flags for encoding
243 pub fn flags(&self) -> u8 {
244 let mut flags: u8 = 0;
245 if self.opt_out {
246 flags |= 0b0000_0001
247 };
248 flags
249 }
250}
251
252impl BinEncodable for NSEC3 {
253 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
254 encoder.emit(self.hash_algorithm().into())?;
255 encoder.emit(self.flags())?;
256 encoder.emit_u16(self.iterations())?;
257 encoder.emit(self.salt().len() as u8)?;
258 encoder.emit_vec(self.salt())?;
259 encoder.emit(self.next_hashed_owner_name().len() as u8)?;
260 encoder.emit_vec(self.next_hashed_owner_name())?;
261 encode_type_bit_maps(encoder, self.type_bit_maps())?;
262
263 Ok(())
264 }
265}
266
267impl<'r> RecordDataDecodable<'r> for NSEC3 {
268 fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> ProtoResult<Self> {
269 let start_idx = decoder.index();
270
271 let hash_algorithm = Nsec3HashAlgorithm::from_u8(
272 decoder.read_u8()?.unverified(/*Algorithm verified as safe*/),
273 )?;
274 let flags: u8 = decoder
275 .read_u8()?
276 .verify_unwrap(|flags| flags & 0b1111_1110 == 0)
277 .map_err(|flags| ProtoError::from(ProtoErrorKind::UnrecognizedNsec3Flags(flags)))?;
278
279 let opt_out: bool = flags & 0b0000_0001 == 0b0000_0001;
280 let iterations: u16 = decoder.read_u16()?.unverified(/*valid as any u16*/);
281
282 // read the salt
283 let salt_len = decoder.read_u8()?.map(|u| u as usize);
284 let salt_len_max = length
285 .map(|u| u as usize)
286 .checked_sub(decoder.index() - start_idx)
287 .map_err(|_| "invalid rdata for salt_len_max")?;
288 let salt_len = salt_len
289 .verify_unwrap(|salt_len| {
290 *salt_len <= salt_len_max.unverified(/*safe in comparison usage*/)
291 })
292 .map_err(|_| ProtoError::from("salt_len exceeds buffer length"))?;
293 let salt: Vec<u8> =
294 decoder.read_vec(salt_len)?.unverified(/*salt is any valid array of bytes*/);
295
296 // read the hashed_owner_name
297 let hash_len = decoder.read_u8()?.map(|u| u as usize);
298 let hash_len_max = length
299 .map(|u| u as usize)
300 .checked_sub(decoder.index() - start_idx)
301 .map_err(|_| "invalid rdata for hash_len_max")?;
302 let hash_len = hash_len
303 .verify_unwrap(|hash_len| {
304 *hash_len <= hash_len_max.unverified(/*safe in comparison usage*/)
305 })
306 .map_err(|_| ProtoError::from("hash_len exceeds buffer length"))?;
307 let next_hashed_owner_name: Vec<u8> =
308 decoder.read_vec(hash_len)?.unverified(/*will fail in usage if invalid*/);
309
310 // read the bitmap
311 let bit_map_len = length
312 .map(|u| u as usize)
313 .checked_sub(decoder.index() - start_idx)
314 .map_err(|_| "invalid rdata length in NSEC3")?;
315 let record_types = decode_type_bit_maps(decoder, bit_map_len)?;
316
317 Ok(Self::new(
318 hash_algorithm,
319 opt_out,
320 iterations,
321 salt,
322 next_hashed_owner_name,
323 record_types,
324 ))
325 }
326}
327
328impl RecordData for NSEC3 {
329 fn try_from_rdata(data: RData) -> Result<Self, RData> {
330 match data {
331 RData::DNSSEC(DNSSECRData::NSEC3(csync)) => Ok(csync),
332 _ => Err(data),
333 }
334 }
335
336 fn try_borrow(data: &RData) -> Option<&Self> {
337 match data {
338 RData::DNSSEC(DNSSECRData::NSEC3(csync)) => Some(csync),
339 _ => None,
340 }
341 }
342
343 fn record_type(&self) -> RecordType {
344 RecordType::NSEC3
345 }
346
347 fn into_rdata(self) -> RData {
348 RData::DNSSEC(DNSSECRData::NSEC3(self))
349 }
350}
351
352/// [RFC 5155](https://tools.ietf.org/html/rfc5155#section-3.3), NSEC3, March 2008
353///
354/// ```text
355/// 3.3. Presentation Format
356///
357/// The presentation format of the RDATA portion is as follows:
358///
359/// o The Hash Algorithm field is represented as an unsigned decimal
360/// integer. The value has a maximum of 255.
361///
362/// o The Flags field is represented as an unsigned decimal integer.
363/// The value has a maximum of 255.
364///
365/// o The Iterations field is represented as an unsigned decimal
366/// integer. The value is between 0 and 65535, inclusive.
367///
368/// o The Salt Length field is not represented.
369///
370/// o The Salt field is represented as a sequence of case-insensitive
371/// hexadecimal digits. Whitespace is not allowed within the
372/// sequence. The Salt field is represented as "-" (without the
373/// quotes) when the Salt Length field has a value of 0.
374///
375/// o The Hash Length field is not represented.
376///
377/// o The Next Hashed Owner Name field is represented as an unpadded
378/// sequence of case-insensitive base32 digits, without whitespace.
379///
380/// o The Type Bit Maps field is represented as a sequence of RR type
381/// mnemonics. When the mnemonic is not known, the TYPE
382/// representation as described in Section 5 of [RFC3597] MUST be
383/// used.
384/// ```
385impl fmt::Display for NSEC3 {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
387 let salt = if self.salt.is_empty() {
388 "-".to_string()
389 } else {
390 data_encoding::HEXUPPER_PERMISSIVE.encode(&self.salt)
391 };
392
393 write!(
394 f,
395 "{alg} {flags} {iterations} {salt} {owner}",
396 alg = u8::from(self.hash_algorithm),
397 flags = self.flags(),
398 iterations = self.iterations,
399 salt = salt,
400 owner = data_encoding::BASE32_NOPAD.encode(&self.next_hashed_owner_name)
401 )?;
402
403 for ty in &self.type_bit_maps {
404 write!(f, " {ty}")?;
405 }
406
407 Ok(())
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 #![allow(clippy::dbg_macro, clippy::print_stdout)]
414
415 use super::*;
416
417 #[test]
418 fn test() {
419 use crate::rr::dnssec::rdata::RecordType;
420
421 let rdata = NSEC3::new(
422 Nsec3HashAlgorithm::SHA1,
423 true,
424 2,
425 vec![1, 2, 3, 4, 5],
426 vec![6, 7, 8, 9, 0],
427 vec![
428 RecordType::A,
429 RecordType::AAAA,
430 RecordType::DS,
431 RecordType::RRSIG,
432 ],
433 );
434
435 let mut bytes = Vec::new();
436 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
437 assert!(rdata.emit(&mut encoder).is_ok());
438 let bytes = encoder.into_bytes();
439
440 println!("bytes: {bytes:?}");
441
442 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
443 let restrict = Restrict::new(bytes.len() as u16);
444 let read_rdata = NSEC3::read_data(&mut decoder, restrict).expect("Decoding error");
445 assert_eq!(rdata, read_rdata);
446 }
447
448 #[test]
449 fn test_dups() {
450 use crate::rr::dnssec::rdata::RecordType;
451
452 let rdata_with_dups = NSEC3::new(
453 Nsec3HashAlgorithm::SHA1,
454 true,
455 2,
456 vec![1, 2, 3, 4, 5],
457 vec![6, 7, 8, 9, 0],
458 vec![
459 RecordType::A,
460 RecordType::AAAA,
461 RecordType::DS,
462 RecordType::AAAA,
463 RecordType::RRSIG,
464 ],
465 );
466
467 let rdata_wo = NSEC3::new(
468 Nsec3HashAlgorithm::SHA1,
469 true,
470 2,
471 vec![1, 2, 3, 4, 5],
472 vec![6, 7, 8, 9, 0],
473 vec![
474 RecordType::A,
475 RecordType::AAAA,
476 RecordType::DS,
477 RecordType::RRSIG,
478 ],
479 );
480
481 let mut bytes = Vec::new();
482 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
483 assert!(rdata_with_dups.emit(&mut encoder).is_ok());
484 let bytes = encoder.into_bytes();
485
486 println!("bytes: {bytes:?}");
487
488 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
489 let restrict = Restrict::new(bytes.len() as u16);
490 let read_rdata = NSEC3::read_data(&mut decoder, restrict).expect("Decoding error");
491 assert_eq!(rdata_wo, read_rdata);
492 }
493}