1 /*
2 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //! parameters used for the nsec3 hash method
18
19 use crate::error::*;
20 use crate::rr::dnssec::Nsec3HashAlgorithm;
21 use crate::serialize::binary::*;
22
23 /// [RFC 5155, NSEC3, March 2008](https://tools.ietf.org/html/rfc5155#section-4)
24 ///
25 /// ```text
26 /// 4. The NSEC3PARAM Resource Record
27 ///
28 /// The NSEC3PARAM RR contains the NSEC3 parameters (hash algorithm,
29 /// flags, iterations, and salt) needed by authoritative servers to
30 /// calculate hashed owner names. The presence of an NSEC3PARAM RR at a
31 /// zone apex indicates that the specified parameters may be used by
32 /// authoritative servers to choose an appropriate set of NSEC3 RRs for
33 /// negative responses. The NSEC3PARAM RR is not used by validators or
34 /// resolvers.
35 ///
36 /// If an NSEC3PARAM RR is present at the apex of a zone with a Flags
37 /// field value of zero, then there MUST be an NSEC3 RR using the same
38 /// hash algorithm, iterations, and salt parameters present at every
39 /// hashed owner name in the zone. That is, the zone MUST contain a
40 /// complete set of NSEC3 RRs with the same hash algorithm, iterations,
41 /// and salt parameters.
42 ///
43 /// The owner name for the NSEC3PARAM RR is the name of the zone apex.
44 ///
45 /// The type value for the NSEC3PARAM RR is 51.
46 ///
47 /// The NSEC3PARAM RR RDATA format is class independent and is described
48 /// below.
49 ///
50 /// The class MUST be the same as the NSEC3 RRs to which this RR refers.
51 ///
52 /// 4.2. NSEC3PARAM RDATA Wire Format
53 ///
54 /// The RDATA of the NSEC3PARAM RR is as shown below:
55 ///
56 /// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
57 /// 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
58 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59 /// | Hash Alg. | Flags | Iterations |
60 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61 /// | Salt Length | Salt /
62 /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63 ///
64 /// Hash Algorithm is a single octet.
65 ///
66 /// Flags field is a single octet.
67 ///
68 /// Iterations is represented as a 16-bit unsigned integer, with the most
69 /// significant bit first.
70 ///
71 /// Salt Length is represented as an unsigned octet. Salt Length
72 /// represents the length of the following Salt field in octets. If the
73 /// value is zero, the Salt field is omitted.
74 ///
75 /// Salt, if present, is encoded as a sequence of binary octets. The
76 /// length of this field is determined by the preceding Salt Length
77 /// field.
78 /// ```
79 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
80 pub struct NSEC3PARAM {
81 hash_algorithm: Nsec3HashAlgorithm,
82 opt_out: bool,
83 iterations: u16,
84 salt: Vec<u8>,
85 }
86
87 impl NSEC3PARAM {
88 /// Constructs a new NSEC3PARAM RData for use in a Resource Record
new( hash_algorithm: Nsec3HashAlgorithm, opt_out: bool, iterations: u16, salt: Vec<u8>, ) -> NSEC3PARAM89 pub fn new(
90 hash_algorithm: Nsec3HashAlgorithm,
91 opt_out: bool,
92 iterations: u16,
93 salt: Vec<u8>,
94 ) -> NSEC3PARAM {
95 NSEC3PARAM {
96 hash_algorithm,
97 opt_out,
98 iterations,
99 salt,
100 }
101 }
102
103 /// [RFC 5155, NSEC3, March 2008](https://tools.ietf.org/html/rfc5155#section-4.1.1)
104 ///
105 /// ```text
106 /// 4.1.1. Hash Algorithm
107 ///
108 /// The Hash Algorithm field identifies the cryptographic hash algorithm
109 /// used to construct the hash-value.
110 ///
111 /// The acceptable values are the same as the corresponding field in the
112 /// NSEC3 RR.
113 /// ```
hash_algorithm(&self) -> Nsec3HashAlgorithm114 pub fn hash_algorithm(&self) -> Nsec3HashAlgorithm {
115 self.hash_algorithm
116 }
117
118 /// [RFC 5155, NSEC3, March 2008](https://tools.ietf.org/html/rfc5155#section-4.1.2)
119 ///
120 /// ```text
121 /// 4.1.2. Flag Fields
122 ///
123 /// The Opt-Out flag is not used and is set to zero.
124 ///
125 /// All other flags are reserved for future use, and must be zero.
126 ///
127 /// NSEC3PARAM RRs with a Flags field value other than zero MUST be
128 /// ignored.
129 /// ```
opt_out(&self) -> bool130 pub fn opt_out(&self) -> bool {
131 self.opt_out
132 }
133
134 /// [RFC 5155, NSEC3, March 2008](https://tools.ietf.org/html/rfc5155#section-4.1.3)
135 ///
136 /// ```text
137 /// 4.1.3. Iterations
138 ///
139 /// The Iterations field defines the number of additional times the hash
140 /// is performed.
141 ///
142 /// Its acceptable values are the same as the corresponding field in the
143 /// NSEC3 RR.
144 /// ```
iterations(&self) -> u16145 pub fn iterations(&self) -> u16 {
146 self.iterations
147 }
148
149 /// [RFC 5155, NSEC3, March 2008](https://tools.ietf.org/html/rfc5155#section-4.1.5)
150 ///
151 /// ```text
152 /// 4.1.5. Salt
153 ///
154 /// The Salt field is appended to the original owner name before hashing.
155 /// ```
salt(&self) -> &[u8]156 pub fn salt(&self) -> &[u8] {
157 &self.salt
158 }
159 }
160
161 /// Read the RData from the given Decoder
read(decoder: &mut BinDecoder) -> ProtoResult<NSEC3PARAM>162 pub fn read(decoder: &mut BinDecoder) -> ProtoResult<NSEC3PARAM> {
163 let hash_algorithm =
164 Nsec3HashAlgorithm::from_u8(decoder.read_u8()?.unverified(/*Algorithm verified as safe*/))?;
165 let flags: u8 = decoder
166 .read_u8()?
167 .verify_unwrap(|flags| flags & 0b1111_1110 == 0)
168 .map_err(|flags| ProtoError::from(ProtoErrorKind::UnrecognizedNsec3Flags(flags)))?;
169
170 let opt_out: bool = flags & 0b0000_0001 == 0b0000_0001;
171 let iterations: u16 = decoder.read_u16()?.unverified(/*valid as any u16*/);
172 let salt_len: usize = decoder
173 .read_u8()?
174 .map(|u| u as usize)
175 .verify_unwrap(|salt_len| *salt_len <= decoder.len())
176 .map_err(|_| ProtoError::from("salt_len exceeds buffer length"))?;
177 let salt: Vec<u8> = decoder.read_vec(salt_len)?.unverified(/*valid as any array of u8*/);
178
179 Ok(NSEC3PARAM::new(hash_algorithm, opt_out, iterations, salt))
180 }
181
182 /// Write the RData from the given Decoder
emit(encoder: &mut BinEncoder, rdata: &NSEC3PARAM) -> ProtoResult<()>183 pub fn emit(encoder: &mut BinEncoder, rdata: &NSEC3PARAM) -> ProtoResult<()> {
184 encoder.emit(rdata.hash_algorithm().into())?;
185 let mut flags: u8 = 0;
186 if rdata.opt_out() {
187 flags |= 0b0000_0001
188 };
189 encoder.emit(flags)?;
190 encoder.emit_u16(rdata.iterations())?;
191 encoder.emit(rdata.salt().len() as u8)?;
192 encoder.emit_vec(rdata.salt())?;
193
194 Ok(())
195 }
196
197 #[cfg(test)]
198 mod tests {
199 #![allow(clippy::dbg_macro, clippy::print_stdout)]
200
201 use super::*;
202
203 #[test]
test()204 pub fn test() {
205 let rdata = NSEC3PARAM::new(Nsec3HashAlgorithm::SHA1, true, 2, vec![1, 2, 3, 4, 5]);
206
207 let mut bytes = Vec::new();
208 let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
209 assert!(emit(&mut encoder, &rdata).is_ok());
210 let bytes = encoder.into_bytes();
211
212 println!("bytes: {:?}", bytes);
213
214 let mut decoder: BinDecoder = BinDecoder::new(bytes);
215 let read_rdata = read(&mut decoder).expect("Decoding error");
216 assert_eq!(rdata, read_rdata);
217 }
218 }
219