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