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