hickory_proto/dnssec/rdata/rrsig.rs
1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! RRSIG type and related implementations
9
10use alloc::vec::Vec;
11use core::{fmt, ops::Deref};
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use crate::{
17 dnssec::Algorithm,
18 error::ProtoResult,
19 rr::{Name, RData, Record, RecordData, RecordDataDecodable, RecordType},
20 serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict},
21};
22
23use super::{DNSSECRData, SIG};
24
25/// RRSIG is really a derivation of the original SIG record data. See SIG for more documentation
26#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
27#[derive(Debug, PartialEq, Eq, Hash, Clone)]
28pub struct RRSIG(SIG);
29
30impl RRSIG {
31 /// Creates a new SIG record data, used for both RRSIG and SIG(0) records.
32 ///
33 /// # Arguments
34 ///
35 /// * `type_covered` - The `RecordType` which this signature covers, should be NULL for SIG(0).
36 /// * `algorithm` - The `Algorithm` used to generate the `signature`.
37 /// * `num_labels` - The number of labels in the name, should be less 1 for *.name labels,
38 /// see `Name::num_labels()`.
39 /// * `original_ttl` - The TTL for the RRSet stored in the zone, should be 0 for SIG(0).
40 /// * `sig_expiration` - Timestamp at which this signature is no longer valid, very important to
41 /// keep this low, < +5 minutes to limit replay attacks.
42 /// * `sig_inception` - Timestamp when this signature was generated.
43 /// * `key_tag` - See the key_tag generation in `rr::dnssec::Signer::key_tag()`.
44 /// * `signer_name` - Domain name of the server which was used to generate the signature.
45 /// * `sig` - signature stored in this record.
46 ///
47 /// # Return value
48 ///
49 /// The new SIG record data.
50 #[allow(clippy::too_many_arguments)]
51 pub fn new(
52 type_covered: RecordType,
53 algorithm: Algorithm,
54 num_labels: u8,
55 original_ttl: u32,
56 sig_expiration: u32,
57 sig_inception: u32,
58 key_tag: u16,
59 signer_name: Name,
60 sig: Vec<u8>,
61 ) -> Self {
62 Self(SIG::new(
63 type_covered,
64 algorithm,
65 num_labels,
66 original_ttl,
67 sig_expiration,
68 sig_inception,
69 key_tag,
70 signer_name,
71 sig,
72 ))
73 }
74
75 /// Returns the authenticated TTL of this RRSIG with a Record.
76 ///
77 /// ```text
78 /// RFC 4035 DNSSEC Protocol Modifications March 2005
79 ///
80 /// If the resolver accepts the RRset as authentic, the validator MUST
81 /// set the TTL of the RRSIG RR and each RR in the authenticated RRset to
82 /// a value no greater than the minimum of:
83 ///
84 /// o the RRset's TTL as received in the response;
85 ///
86 /// o the RRSIG RR's TTL as received in the response;
87 ///
88 /// o the value in the RRSIG RR's Original TTL field; and
89 ///
90 /// o the difference of the RRSIG RR's Signature Expiration time and the
91 /// current time.
92 /// ```
93 ///
94 /// See [RFC 4035, section 5.3.3](https://datatracker.ietf.org/doc/html/rfc4035#section-5.3.3).
95 ///
96 pub fn authenticated_ttl(&self, record: &Record, current_time: u32) -> u32 {
97 record
98 .ttl()
99 .min(self.original_ttl())
100 .min(self.sig_expiration().0.saturating_sub(current_time))
101 }
102}
103
104impl Deref for RRSIG {
105 type Target = SIG;
106
107 fn deref(&self) -> &Self::Target {
108 &self.0
109 }
110}
111
112impl BinEncodable for RRSIG {
113 /// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
114 ///
115 /// This is accurate for all currently known name records.
116 ///
117 /// ```text
118 /// 6.2. Canonical RR Form
119 ///
120 /// For the purposes of DNS security, the canonical form of an RR is the
121 /// wire format of the RR where:
122 ///
123 /// ...
124 ///
125 /// 3. if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
126 /// HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
127 /// SRV, DNAME, A6, RRSIG, or (rfc6840 removes NSEC), all uppercase
128 /// US-ASCII letters in the DNS names contained within the RDATA are replaced
129 /// by the corresponding lowercase US-ASCII letters;
130 /// ```
131 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
132 self.0.emit(encoder)
133 }
134}
135
136impl<'r> RecordDataDecodable<'r> for RRSIG {
137 fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> ProtoResult<Self> {
138 SIG::read_data(decoder, length).map(Self)
139 }
140}
141
142impl RecordData for RRSIG {
143 fn try_from_rdata(data: RData) -> Result<Self, RData> {
144 match data {
145 RData::DNSSEC(DNSSECRData::RRSIG(csync)) => Ok(csync),
146 _ => Err(data),
147 }
148 }
149
150 fn try_borrow(data: &RData) -> Option<&Self> {
151 match data {
152 RData::DNSSEC(DNSSECRData::RRSIG(csync)) => Some(csync),
153 _ => None,
154 }
155 }
156
157 fn record_type(&self) -> RecordType {
158 RecordType::RRSIG
159 }
160
161 fn into_rdata(self) -> RData {
162 RData::DNSSEC(DNSSECRData::RRSIG(self))
163 }
164}
165
166impl fmt::Display for RRSIG {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
168 write!(f, "{}", self.0)
169 }
170}