1 /*
2  * Copyright (C) 2016 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 //! pointer record from parent zone to child zone for dnskey proof
18 
19 use std::fmt::{self, Display, Formatter};
20 
21 use crate::error::*;
22 use crate::rr::dnssec::{Algorithm, DigestType};
23 use crate::serialize::binary::*;
24 
25 use crate::rr::dnssec::rdata::DNSKEY;
26 use crate::rr::Name;
27 
28 /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5)
29 ///
30 /// ```text
31 /// 5.1.  DS RDATA Wire Format
32 ///
33 ///    The RDATA for a DS RR consists of a 2 octet Key Tag field, a 1 octet
34 ///    Algorithm field, a 1 octet Digest Type field, and a Digest field.
35 ///
36 ///                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
37 ///     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
38 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 ///    |           Key Tag             |  Algorithm    |  Digest Type  |
40 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 ///    /                                                               /
42 ///    /                            Digest                             /
43 ///    /                                                               /
44 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 ///
46 /// 5.2.  Processing of DS RRs When Validating Responses
47 ///
48 ///    The DS RR links the authentication chain across zone boundaries, so
49 ///    the DS RR requires extra care in processing.  The DNSKEY RR referred
50 ///    to in the DS RR MUST be a DNSSEC zone key.  The DNSKEY RR Flags MUST
51 ///    have Flags bit 7 set.  If the DNSKEY flags do not indicate a DNSSEC
52 ///    zone key, the DS RR (and the DNSKEY RR it references) MUST NOT be
53 ///    used in the validation process.
54 ///
55 /// 5.3.  The DS RR Presentation Format
56 ///
57 ///    The presentation format of the RDATA portion is as follows:
58 ///
59 ///    The Key Tag field MUST be represented as an unsigned decimal integer.
60 ///
61 ///    The Algorithm field MUST be represented either as an unsigned decimal
62 ///    integer or as an algorithm mnemonic specified in Appendix A.1.
63 ///
64 ///    The Digest Type field MUST be represented as an unsigned decimal
65 ///    integer.
66 ///
67 ///    The Digest MUST be represented as a sequence of case-insensitive
68 ///    hexadecimal digits.  Whitespace is allowed within the hexadecimal
69 ///    text.
70 /// ```
71 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
72 pub struct DS {
73     key_tag: u16,
74     algorithm: Algorithm,
75     digest_type: DigestType,
76     digest: Vec<u8>,
77 }
78 
79 impl DS {
80     /// Constructs a new DS RData
81     ///
82     /// # Arguments
83     ///
84     /// * `key_tag` - the key_tag associated to the DNSKEY
85     /// * `algorithm` - algorithm as specified in the DNSKEY
86     /// * `digest_type` - hash algorithm used to validate the DNSKEY
87     /// * `digest` - hash of the DNSKEY
88     ///
89     /// # Returns
90     ///
91     /// the DS RDATA for use in a Resource Record
new(key_tag: u16, algorithm: Algorithm, digest_type: DigestType, digest: Vec<u8>) -> DS92     pub fn new(key_tag: u16, algorithm: Algorithm, digest_type: DigestType, digest: Vec<u8>) -> DS {
93         DS {
94             key_tag,
95             algorithm,
96             digest_type,
97             digest,
98         }
99     }
100 
101     /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
102     ///
103     /// ```text
104     /// 5.1.1.  The Key Tag Field
105     ///
106     ///    The Key Tag field lists the key tag of the DNSKEY RR referred to by
107     ///    the DS record, in network byte order.
108     ///
109     ///    The Key Tag used by the DS RR is identical to the Key Tag used by
110     ///    RRSIG RRs.  Appendix B describes how to compute a Key Tag.
111     /// ```
key_tag(&self) -> u16112     pub fn key_tag(&self) -> u16 {
113         self.key_tag
114     }
115 
116     /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
117     ///
118     /// ```text
119     /// 5.1.2.  The Algorithm Field
120     ///
121     ///    The Algorithm field lists the algorithm number of the DNSKEY RR
122     ///    referred to by the DS record.
123     ///
124     ///    The algorithm number used by the DS RR is identical to the algorithm
125     ///    number used by RRSIG and DNSKEY RRs.  Appendix A.1 lists the
126     ///    algorithm number types.
127     /// ```
algorithm(&self) -> Algorithm128     pub fn algorithm(&self) -> Algorithm {
129         self.algorithm
130     }
131 
132     /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
133     ///
134     /// ```text
135     /// 5.1.3.  The Digest Type Field
136     ///
137     ///    The DS RR refers to a DNSKEY RR by including a digest of that DNSKEY
138     ///    RR.  The Digest Type field identifies the algorithm used to construct
139     ///    the digest.  Appendix A.2 lists the possible digest algorithm types.
140     /// ```
digest_type(&self) -> DigestType141     pub fn digest_type(&self) -> DigestType {
142         self.digest_type
143     }
144 
145     /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.1.1)
146     ///
147     /// ```text
148     /// 5.1.4.  The Digest Field
149     ///
150     ///    The DS record refers to a DNSKEY RR by including a digest of that
151     ///    DNSKEY RR.
152     ///
153     ///    The digest is calculated by concatenating the canonical form of the
154     ///    fully qualified owner name of the DNSKEY RR with the DNSKEY RDATA,
155     ///    and then applying the digest algorithm.
156     ///
157     ///      digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
158     ///
159     ///       "|" denotes concatenation
160     ///
161     ///      DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
162     ///
163     ///    The size of the digest may vary depending on the digest algorithm and
164     ///    DNSKEY RR size.  As of the time of this writing, the only defined
165     ///    digest algorithm is SHA-1, which produces a 20 octet digest.
166     /// ```
digest(&self) -> &[u8]167     pub fn digest(&self) -> &[u8] {
168         &self.digest
169     }
170 
171     /// Validates that a given DNSKEY is covered by the DS record.
172     ///
173     /// # Return
174     ///
175     /// true if and only if the DNSKEY is covered by the DS record.
176     #[cfg(any(feature = "openssl", feature = "ring"))]
covers(&self, name: &Name, key: &DNSKEY) -> ProtoResult<bool>177     pub fn covers(&self, name: &Name, key: &DNSKEY) -> ProtoResult<bool> {
178         key.to_digest(name, self.digest_type())
179             .map(|hash| hash.as_ref() == self.digest())
180     }
181 
182     /// This will always return an error unless the Ring or OpenSSL features are enabled
183     #[cfg(not(any(feature = "openssl", feature = "ring")))]
covers(&self, _: &Name, _: &DNSKEY) -> ProtoResult<bool>184     pub fn covers(&self, _: &Name, _: &DNSKEY) -> ProtoResult<bool> {
185         Err("Ring or OpenSSL must be enabled for this feature".into())
186     }
187 }
188 
189 /// Read the RData from the given Decoder
read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<DS>190 pub fn read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<DS> {
191     let start_idx = decoder.index();
192 
193     let key_tag: u16 = decoder.read_u16()?.unverified(/*key_tag is valid as any u16*/);
194     let algorithm: Algorithm = Algorithm::read(decoder)?;
195     let digest_type: DigestType =
196         DigestType::from_u8(decoder.read_u8()?.unverified(/*DigestType is verified as safe*/))?;
197 
198     let bytes_read = decoder.index() - start_idx;
199     let left: usize = rdata_length
200         .map(|u| u as usize)
201         .checked_sub(bytes_read)
202         .map_err(|_| ProtoError::from("invalid rdata length in DS"))?
203         .unverified(/*used only as length safely*/);
204     let digest =
205         decoder.read_vec(left)?.unverified(/*the byte array will fail in usage if invalid*/);
206 
207     Ok(DS::new(key_tag, algorithm, digest_type, digest))
208 }
209 
210 /// Write the RData from the given Decoder
emit(encoder: &mut BinEncoder<'_>, rdata: &DS) -> ProtoResult<()>211 pub fn emit(encoder: &mut BinEncoder<'_>, rdata: &DS) -> ProtoResult<()> {
212     encoder.emit_u16(rdata.key_tag())?;
213     rdata.algorithm().emit(encoder)?; // always 3 for now
214     encoder.emit(rdata.digest_type().into())?;
215     encoder.emit_vec(rdata.digest())?;
216 
217     Ok(())
218 }
219 
220 /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5.3)
221 ///
222 /// ```text
223 /// 5.3.  The DS RR Presentation Format
224 ///
225 ///    The presentation format of the RDATA portion is as follows:
226 ///
227 ///    The Key Tag field MUST be represented as an unsigned decimal integer.
228 ///
229 ///    The Algorithm field MUST be represented either as an unsigned decimal
230 ///    integer or as an algorithm mnemonic specified in Appendix A.1.
231 ///
232 ///    The Digest Type field MUST be represented as an unsigned decimal
233 ///    integer.
234 ///
235 ///    The Digest MUST be represented as a sequence of case-insensitive
236 ///    hexadecimal digits.  Whitespace is allowed within the hexadecimal
237 ///    text.
238 ///
239 /// 5.4.  DS RR Example
240 ///
241 ///    The following example shows a DNSKEY RR and its corresponding DS RR.
242 ///
243 ///    dskey.example.com. 86400 IN DNSKEY 256 3 5 ( AQOeiiR0GOMYkDshWoSKz9Xz
244 ///                                              fwJr1AYtsmx3TGkJaNXVbfi/
245 ///                                              2pHm822aJ5iI9BMzNXxeYCmZ
246 ///                                              DRD99WYwYqUSdjMmmAphXdvx
247 ///                                              egXd/M5+X7OrzKBaMbCVdFLU
248 ///                                              Uh6DhweJBjEVv5f2wwjM9Xzc
249 ///                                              nOf+EPbtG9DMBmADjFDc2w/r
250 ///                                              ljwvFw==
251 ///                                              ) ;  key id = 60485
252 ///
253 ///    dskey.example.com. 86400 IN DS 60485 5 1 ( 2BB183AF5F22588179A53B0A
254 ///                                               98631FAD1A292118 )
255 ///
256 ///    The first four text fields specify the name, TTL, Class, and RR type
257 ///    (DS).  Value 60485 is the key tag for the corresponding
258 ///    "dskey.example.com." DNSKEY RR, and value 5 denotes the algorithm
259 ///    used by this "dskey.example.com." DNSKEY RR.  The value 1 is the
260 ///    algorithm used to construct the digest, and the rest of the RDATA
261 ///    text is the digest in hexadecimal.
262 /// ```
263 impl Display for DS {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error>264     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
265         write!(
266             f,
267             "{tag} {alg} {ty} {digest}",
268             tag = self.key_tag,
269             alg = u8::from(self.algorithm),
270             ty = u8::from(self.digest_type),
271             digest = data_encoding::HEXUPPER_PERMISSIVE.encode(&self.digest)
272         )
273     }
274 }
275 
276 #[cfg(test)]
277 mod tests {
278     #![allow(clippy::dbg_macro, clippy::print_stdout)]
279 
280     use super::*;
281 
282     #[test]
test()283     fn test() {
284         let rdata = DS::new(
285             0xF00F,
286             Algorithm::RSASHA256,
287             DigestType::SHA256,
288             vec![5, 6, 7, 8],
289         );
290 
291         let mut bytes = Vec::new();
292         let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
293         assert!(emit(&mut encoder, &rdata).is_ok());
294         let bytes = encoder.into_bytes();
295 
296         println!("bytes: {:?}", bytes);
297 
298         let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
299         let restrict = Restrict::new(bytes.len() as u16);
300         let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
301         assert_eq!(rdata, read_rdata);
302     }
303 
304     #[test]
305     #[cfg(any(feature = "openssl", feature = "ring"))]
test_covers()306     pub(crate) fn test_covers() {
307         use crate::rr::dnssec::rdata::DNSKEY;
308 
309         let name = Name::parse("www.example.com.", None).unwrap();
310 
311         let dnskey_rdata = DNSKEY::new(true, true, false, Algorithm::RSASHA256, vec![1, 2, 3, 4]);
312         let ds_rdata = DS::new(
313             0,
314             Algorithm::RSASHA256,
315             DigestType::SHA256,
316             dnskey_rdata
317                 .to_digest(&name, DigestType::SHA256)
318                 .unwrap()
319                 .as_ref()
320                 .to_owned(),
321         );
322 
323         assert!(ds_rdata.covers(&name, &dnskey_rdata).unwrap());
324     }
325 }
326