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