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}