881adc672182650b2f8f7c1786cdb6a6e5c7485a
[dnssec-prover] / src / rr.rs
1 //! Resource Records are the fundamental type in the DNS - individual records mapping a name to
2 //! some data.
3 //!
4 //! This module holds structs and utilities for the Resource Records supported by this crate.
5
6 use alloc::vec::Vec;
7 use alloc::string::String;
8 use alloc::borrow::ToOwned;
9
10 use crate::ser::*;
11
12 /// A valid domain name.
13 ///
14 /// It must end with a ".", be no longer than 255 bytes, consist of only printable ASCII
15 /// characters and each label may be no longer than 63 bytes.
16 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
17 pub struct Name(String);
18 impl Name {
19         /// Gets the underlying human-readable domain name
20         pub fn as_str(&self) -> &str { &self.0 }
21 }
22 impl core::ops::Deref for Name {
23         type Target = str;
24         fn deref(&self) -> &str { &self.0 }
25 }
26 impl TryFrom<String> for Name {
27         type Error = ();
28         fn try_from(s: String) -> Result<Name, ()> {
29                 if s.is_empty() { return Err(()); }
30                 if *s.as_bytes().last().unwrap_or(&0) != b"."[0] { return Err(()); }
31                 if s.len() > 255 { return Err(()); }
32                 if s.chars().any(|c| !c.is_ascii_graphic() && c != '.' && c != '-') { return Err(()); }
33                 for label in s.split(".") {
34                         if label.len() > 63 { return Err(()); }
35                 }
36
37                 Ok(Name(s))
38         }
39 }
40 impl TryFrom<&str> for Name {
41         type Error = ();
42         fn try_from(s: &str) -> Result<Name, ()> {
43                 Self::try_from(s.to_owned())
44         }
45 }
46
47 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
48 /// A supported Resource Record
49 ///
50 /// Note that we only currently support a handful of RR types as needed to generate and validate
51 /// TXT or TLSA record proofs.
52 pub enum RR {
53         /// A text resource record
54         Txt(Txt),
55         /// A TLS Certificate Association resource record
56         TLSA(TLSA),
57         /// A Canonical Name record
58         CName(CName),
59         /// A DNS (Public) Key resource record
60         DnsKey(DnsKey),
61         /// A Delegated Signer resource record
62         DS(DS),
63         /// A Resource Record Signature record
64         RRSig(RRSig),
65 }
66 impl RR {
67         /// Gets the name this record refers to.
68         pub fn name(&self) -> &Name {
69                 match self {
70                         RR::Txt(rr) => &rr.name,
71                         RR::CName(rr) => &rr.name,
72                         RR::TLSA(rr) => &rr.name,
73                         RR::DnsKey(rr) => &rr.name,
74                         RR::DS(rr) => &rr.name,
75                         RR::RRSig(rr) => &rr.name,
76                 }
77         }
78         fn ty(&self) -> u16 {
79                 match self {
80                         RR::Txt(_) => Txt::TYPE,
81                         RR::CName(_) => CName::TYPE,
82                         RR::TLSA(_) => TLSA::TYPE,
83                         RR::DnsKey(_) => DnsKey::TYPE,
84                         RR::DS(_) => DS::TYPE,
85                         RR::RRSig(_) => RRSig::TYPE,
86                 }
87         }
88         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
89                 match self {
90                         RR::Txt(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
91                         RR::CName(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
92                         RR::TLSA(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
93                         RR::DnsKey(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
94                         RR::DS(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
95                         RR::RRSig(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
96                 }
97         }
98 }
99 impl From<Txt> for RR { fn from(txt: Txt) -> RR { RR::Txt(txt) } }
100 impl From<CName> for RR { fn from(cname: CName) -> RR { RR::CName(cname) } }
101 impl From<TLSA> for RR { fn from(tlsa: TLSA) -> RR { RR::TLSA(tlsa) } }
102 impl From<DnsKey> for RR { fn from(dnskey: DnsKey) -> RR { RR::DnsKey(dnskey) } }
103 impl From<DS> for RR { fn from(ds: DS) -> RR { RR::DS(ds) } }
104 impl From<RRSig> for RR { fn from(rrsig: RRSig) -> RR { RR::RRSig(rrsig) } }
105
106 pub(crate) trait StaticRecord : Ord + Sized {
107         // http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
108         const TYPE: u16;
109         fn name(&self) -> &Name;
110         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>);
111         fn read_from_data(name: Name, data: &[u8]) -> Result<Self, ()>;
112 }
113 /// A trait describing a resource record (including the [`RR`] enum).
114 pub trait Record : Ord {
115         /// The resource record type, as maintained by IANA.
116         ///
117         /// Current assignments can be found at
118         /// <http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4>
119         fn ty(&self) -> u16;
120         /// The name this record is at.
121         fn name(&self) -> &Name;
122         /// Writes the data of this record, prefixed by a u16 length, to the given `Vec`.
123         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>);
124 }
125 impl<RR: StaticRecord> Record for RR {
126         fn ty(&self) -> u16 { RR::TYPE }
127         fn name(&self) -> &Name { RR::name(self) }
128         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
129                 RR::write_u16_len_prefixed_data(self, out)
130         }
131 }
132 impl Record for RR {
133         fn ty(&self) -> u16 { self.ty() }
134         fn name(&self) -> &Name { self.name() }
135         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
136                 self.write_u16_len_prefixed_data(out)
137         }
138 }
139
140 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] // TODO: ord is wrong cause need to consider len first, maybe
141 /// A text resource record, containing arbitrary text data
142 pub struct Txt {
143         /// The name this record is at.
144         pub name: Name,
145         /// The text record itself.
146         ///
147         /// While this is generally UTF-8-valid, there is no specific requirement that it be, and thus
148         /// is an arbitrary series of bytes here.
149         pub data: Vec<u8>,
150 }
151 impl StaticRecord for Txt {
152         const TYPE: u16 = 16;
153         fn name(&self) -> &Name { &self.name }
154         fn read_from_data(name: Name, mut data: &[u8]) -> Result<Self, ()> {
155                 let mut parsed_data = Vec::with_capacity(data.len() - 1);
156                 while !data.is_empty() {
157                         let len = read_u8(&mut data)? as usize;
158                         if data.len() < len { return Err(()); }
159                         parsed_data.extend_from_slice(&data[..len]);
160                         data = &data[len..];
161                 }
162                 Ok(Txt { name, data: parsed_data })
163         }
164         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
165                 let len = (self.data.len() + self.data.len() / 255 + 1) as u16;
166                 out.extend_from_slice(&len.to_be_bytes());
167
168                 let mut data_write = &self.data[..];
169                 out.extend_from_slice(&[data_write.len().try_into().unwrap_or(255)]);
170                 while !data_write.is_empty() {
171                         let split_pos = core::cmp::min(255, data_write.len());
172                         out.extend_from_slice(&data_write[..split_pos]);
173                         data_write = &data_write[split_pos..];
174                         if !data_write.is_empty() {
175                                 out.extend_from_slice(&[data_write.len().try_into().unwrap_or(255)]);
176                         }
177                 }
178         }
179 }
180
181 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
182 /// A TLS Certificate Association resource record containing information about the TLS certificate
183 /// which should be expected when communicating with the host at the given name.
184 ///
185 /// See <https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities#TLSA_RR> for more
186 /// info.
187 pub struct TLSA {
188         /// The name this record is at.
189         pub name: Name,
190         /// The type of constraint on the TLS certificate(s) used which should be enforced by this
191         /// record.
192         pub cert_usage: u8,
193         /// Whether to match on the full certificate, or only the public key.
194         pub selector: u8,
195         /// The type of data included which is used to match the TLS certificate(s).
196         pub data_ty: u8,
197         /// The certificate data or hash of the certificate data itself.
198         pub data: Vec<u8>,
199 }
200 impl StaticRecord for TLSA {
201         const TYPE: u16 = 52;
202         fn name(&self) -> &Name { &self.name }
203         fn read_from_data(name: Name, mut data: &[u8]) -> Result<Self, ()> {
204                 Ok(TLSA {
205                         name, cert_usage: read_u8(&mut data)?, selector: read_u8(&mut data)?,
206                         data_ty: read_u8(&mut data)?, data: data.to_vec(),
207                 })
208         }
209         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
210                 let len = 3 + self.data.len();
211                 out.extend_from_slice(&(len as u16).to_be_bytes());
212                 out.extend_from_slice(&[self.cert_usage, self.selector, self.data_ty]);
213                 out.extend_from_slice(&self.data);
214         }
215 }
216
217 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
218 /// A Canonical Name resource record, referring all queries for this name to another name.
219 pub struct CName {
220         /// The name this record is at.
221         pub name: Name,
222         /// The canonical name.
223         ///
224         /// A resolver should use this name when looking up any further records for [`Self::name`].
225         pub canonical_name: Name,
226 }
227 impl StaticRecord for CName {
228         const TYPE: u16 = 5;
229         fn name(&self) -> &Name { &self.name }
230         fn read_from_data(name: Name, mut data: &[u8]) -> Result<Self, ()> {
231                 Ok(CName { name, canonical_name: read_name(&mut data)? })
232         }
233         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
234                 let len: u16 = name_len(&self.canonical_name);
235                 out.extend_from_slice(&len.to_be_bytes());
236                 write_name(out, &self.canonical_name);
237         }
238 }
239
240 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
241 /// A public key resource record which can be used to validate [`RRSig`]s.
242 pub struct DnsKey {
243         /// The name this record is at.
244         pub name: Name,
245         /// Flags which constrain the usage of this public key.
246         pub flags: u16,
247         /// The protocol this key is used for (protocol `3` is DNSSEC). 
248         pub protocol: u8,
249         /// The algorithm which this public key uses to sign data.
250         pub alg: u8,
251         /// The public key itself.
252         pub pubkey: Vec<u8>,
253 }
254 impl StaticRecord for DnsKey {
255         const TYPE: u16 = 48;
256         fn name(&self) -> &Name { &self.name }
257         fn read_from_data(name: Name, mut data: &[u8]) -> Result<Self, ()> {
258                 Ok(DnsKey {
259                         name, flags: read_u16(&mut data)?, protocol: read_u8(&mut data)?,
260                         alg: read_u8(&mut data)?, pubkey: data.to_vec(),
261                 })
262         }
263         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
264                 let len = 2 + 1 + 1 + self.pubkey.len();
265                 out.extend_from_slice(&(len as u16).to_be_bytes());
266                 out.extend_from_slice(&self.flags.to_be_bytes());
267                 out.extend_from_slice(&self.protocol.to_be_bytes());
268                 out.extend_from_slice(&self.alg.to_be_bytes());
269                 out.extend_from_slice(&self.pubkey);
270         }
271 }
272 impl DnsKey {
273         /// A short (non-cryptographic) digest which can be used to refer to this [`DnsKey`].
274         pub fn key_tag(&self) -> u16 {
275                 let mut res = u32::from(self.flags);
276                 res += u32::from(self.protocol) << 8;
277                 res += u32::from(self.alg);
278                 for (idx, b) in self.pubkey.iter().enumerate() {
279                         if idx % 2 == 0 {
280                                 res += u32::from(*b) << 8;
281                         } else {
282                                 res += u32::from(*b);
283                         }
284                 }
285                 res += (res >> 16) & 0xffff;
286                 (res & 0xffff) as u16
287         }
288 }
289
290 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
291 /// A Delegation Signer resource record which indicates that some alternative [`DnsKey`] can sign
292 /// for records in the zone which matches [`DS::name`].
293 pub struct DS {
294         /// The name this record is at.
295         ///
296         /// This is also the zone that a [`DnsKey`] which matches the [`Self::digest`] can sign for.
297         pub name: Name,
298         /// A short tag which describes the matching [`DnsKey`].
299         ///
300         /// This matches the [`DnsKey::key_tag`] for the [`DnsKey`] which is referred to by this
301         /// [`DS`].
302         pub key_tag: u16,
303         /// The algorithm which the [`DnsKey`] referred to by this [`DS`] uses.
304         ///
305         /// This matches the [`DnsKey::alg`] field in the referred-to [`DnsKey`].
306         pub alg: u8,
307         /// The type of digest used to hash the referred-to [`DnsKey`].
308         pub digest_type: u8,
309         /// The digest itself.
310         pub digest: Vec<u8>,
311 }
312 impl StaticRecord for DS {
313         const TYPE: u16 = 43;
314         fn name(&self) -> &Name { &self.name }
315         fn read_from_data(name: Name, mut data: &[u8]) -> Result<Self, ()> {
316                 Ok(DS {
317                         name, key_tag: read_u16(&mut data)?, alg: read_u8(&mut data)?,
318                         digest_type: read_u8(&mut data)?, digest: data.to_vec(),
319                 })
320         }
321         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
322                 let len = 2 + 1 + 1 + self.digest.len();
323                 out.extend_from_slice(&(len as u16).to_be_bytes());
324                 out.extend_from_slice(&self.key_tag.to_be_bytes());
325                 out.extend_from_slice(&self.alg.to_be_bytes());
326                 out.extend_from_slice(&self.digest_type.to_be_bytes());
327                 out.extend_from_slice(&self.digest);
328         }
329 }
330
331 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
332 /// A Resource Record (set) Signature resource record. This contains a signature over all the
333 /// resources records of the given type at the given name.
334 pub struct RRSig {
335         /// The name this record is at.
336         ///
337         /// This is also the name of any records which this signature is covering (ignoring wildcards).
338         pub name: Name,
339         /// The resource record type which this [`RRSig`] is signing.
340         ///
341         /// All resources records of this type at the same name as [`Self::name`] must be signed by
342         /// this [`RRSig`].
343         pub ty: u16,
344         /// The algorithm which is being used to sign.
345         ///
346         /// This must match the [`DnsKey::alg`] field in the [`DnsKey`] being used to sign.
347         pub alg: u8,
348         /// The number of labels in the name of the records that this signature is signing.
349         // TODO: Describe this better in terms of wildcards
350         pub labels: u8,
351         /// The TTL of the records which this [`RRSig`] is signing.
352         pub orig_ttl: u32,
353         /// The expiration (as a UNIX timestamp) of this signature.
354         pub expiration: u32,
355         /// The time (as a UNIX timestamp) at which this signature becomes valid.
356         pub inception: u32,
357         /// A short tag which describes the matching [`DnsKey`].
358         ///
359         /// This matches the [`DnsKey::key_tag`] for the [`DnsKey`] which created this signature.
360         pub key_tag: u16,
361         /// The [`DnsKey::name`] in the [`DnsKey`] which created this signature.
362         ///
363         /// This must be a parent of the [`Self::name`].
364         pub key_name: Name,
365         /// The signature itself.
366         pub signature: Vec<u8>,
367 }
368 impl StaticRecord for RRSig {
369         const TYPE: u16 = 46;
370         fn name(&self) -> &Name { &self.name }
371         fn read_from_data(name: Name, mut data: &[u8]) -> Result<Self, ()> {
372                 Ok(RRSig {
373                         name, ty: read_u16(&mut data)?, alg: read_u8(&mut data)?,
374                         labels: read_u8(&mut data)?, orig_ttl: read_u32(&mut data)?,
375                         expiration: read_u32(&mut data)?, inception: read_u32(&mut data)?,
376                         key_tag: read_u16(&mut data)?, key_name: read_name(&mut data)?,
377                         signature: data.to_vec(),
378                 })
379         }
380         fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
381                 let len = 2 + 1 + 1 + 4*3 + 2 + name_len(&self.key_name) + self.signature.len() as u16;
382                 out.extend_from_slice(&len.to_be_bytes());
383                 out.extend_from_slice(&self.ty.to_be_bytes());
384                 out.extend_from_slice(&self.alg.to_be_bytes());
385                 out.extend_from_slice(&self.labels.to_be_bytes());
386                 out.extend_from_slice(&self.orig_ttl.to_be_bytes());
387                 out.extend_from_slice(&self.expiration.to_be_bytes());
388                 out.extend_from_slice(&self.inception.to_be_bytes());
389                 out.extend_from_slice(&self.key_tag.to_be_bytes());
390                 write_name(out, &self.key_name);
391                 out.extend_from_slice(&self.signature);
392         }
393 }