Skip to main content

hickory_proto/rr/rdata/
tsig.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//! TSIG for secret key authentication of transaction
9#![allow(clippy::use_self)]
10
11#[cfg(feature = "__dnssec")]
12use alloc::boxed::Box;
13use alloc::vec::Vec;
14use core::{convert::TryInto, fmt};
15
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18
19#[cfg(feature = "__dnssec")]
20use crate::dnssec::{DnsSecError, ring_like::hmac};
21#[cfg(feature = "__dnssec")]
22use crate::op::{Header, Message, Query};
23#[cfg(feature = "__dnssec")]
24use crate::rr::tsig::TSigner;
25use crate::{
26    error::{ProtoError, ProtoResult},
27    rr::{
28        Name, Record, RecordData, RecordDataDecodable, dns_class::DNSClass, rdata::sshfp,
29        record_data::RData, record_type::RecordType,
30    },
31    serialize::binary::{
32        BinDecodable, BinDecoder, BinEncodable, BinEncoder, DecodeError, NameEncoding,
33        RDataEncoding, Restrict, RestrictedMath,
34    },
35};
36
37/// [RFC 8945, Secret Key Transaction Authentication for DNS](https://tools.ietf.org/html/rfc8945#section-4.2)
38///
39/// ```text
40///   4.2.  TSIG Record Format
41///
42///   The fields of the TSIG RR are described below.  All multi-octet
43///   integers in the record are sent in network byte order (see
44///   Section 2.3.2 of [RFC1035]).
45///
46///   NAME:  The name of the key used, in domain name syntax.  The name
47///      should reflect the names of the hosts and uniquely identify the
48///      key among a set of keys these two hosts may share at any given
49///      time.  For example, if hosts A.site.example and B.example.net
50///      share a key, possibilities for the key name include
51///      <id>.A.site.example, <id>.B.example.net, and
52///      <id>.A.site.example.B.example.net.  It should be possible for more
53///      than one key to be in simultaneous use among a set of interacting
54///      hosts.  This allows for periodic key rotation as per best
55///      operational practices, as well as algorithm agility as indicated
56///      by [RFC7696].
57///
58///      The name may be used as a local index to the key involved, but it
59///      is recommended that it be globally unique.  Where a key is just
60///      shared between two hosts, its name actually need only be
61///      meaningful to them, but it is recommended that the key name be
62///      mnemonic and incorporate the names of participating agents or
63///      resources as suggested above.
64///
65///   TYPE:  This MUST be TSIG (250: Transaction SIGnature).
66///
67///   CLASS:  This MUST be ANY.
68///
69///   TTL:  This MUST be 0.
70///
71///   RDLENGTH:  (variable)
72///
73///   RDATA:  The RDATA for a TSIG RR consists of a number of fields,
74///      described below:
75///
76///                            1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
77///        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
78///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79///       /                         Algorithm Name                        /
80///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81///       |                                                               |
82///       |          Time Signed          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
83///       |                               |            Fudge              |
84///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85///       |          MAC Size             |                               /
86///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+             MAC               /
87///       /                                                               /
88///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
89///       |          Original ID          |            Error              |
90///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91///       |          Other Len            |                               /
92///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+           Other Data          /
93///       /                                                               /
94///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95///
96///   The contents of the RDATA fields are:
97///
98///   Algorithm Name:
99///      an octet sequence identifying the TSIG algorithm in the domain
100///      name syntax.  (Allowed names are listed in Table 3.)  The name is
101///      stored in the DNS name wire format as described in [RFC1034].  As
102///      per [RFC3597], this name MUST NOT be compressed.
103///
104///   Time Signed:
105///      an unsigned 48-bit integer containing the time the message was
106///      signed as seconds since 00:00 on 1970-01-01 UTC, ignoring leap
107///      seconds.
108///
109///   Fudge:
110///      an unsigned 16-bit integer specifying the allowed time difference
111///      in seconds permitted in the Time Signed field.
112///
113///   MAC Size:
114///      an unsigned 16-bit integer giving the length of the MAC field in
115///      octets.  Truncation is indicated by a MAC Size less than the size
116///      of the keyed hash produced by the algorithm specified by the
117///      Algorithm Name.
118///
119///   MAC:
120///      a sequence of octets whose contents are defined by the TSIG
121///      algorithm used, possibly truncated as specified by the MAC Size.
122///      The length of this field is given by the MAC Size.  Calculation of
123///      the MAC is detailed in Section 4.3.
124///
125///   Original ID:
126///      an unsigned 16-bit integer holding the message ID of the original
127///      request message.  For a TSIG RR on a request, it is set equal to
128///      the DNS message ID.  In a TSIG attached to a response -- or in
129///      cases such as the forwarding of a dynamic update request -- the
130///      field contains the ID of the original DNS request.
131///
132///   Error:
133///      in responses, an unsigned 16-bit integer containing the extended
134///      RCODE covering TSIG processing.  In requests, this MUST be zero.
135///
136///   Other Len:
137///      an unsigned 16-bit integer specifying the length of the Other Data
138///      field in octets.
139///
140///   Other Data:
141///      additional data relevant to the TSIG record.  In responses, this
142///      will be empty (i.e., Other Len will be zero) unless the content of
143///      the Error field is BADTIME, in which case it will be a 48-bit
144///      unsigned integer containing the server's current time as the
145///      number of seconds since 00:00 on 1970-01-01 UTC, ignoring leap
146///      seconds (see Section 5.2.3).  This document assigns no meaning to
147///      its contents in requests.
148/// ```
149#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
150#[derive(Debug, PartialEq, Eq, Hash, Clone)]
151#[non_exhaustive]
152pub struct TSIG {
153    /// The algorithm used for the authentication code
154    pub algorithm: TsigAlgorithm,
155
156    /// The time this TSIG was generated at
157    pub time: u64,
158
159    /// The max delta from `time` for remote to accept the signature
160    pub fudge: u16,
161
162    /// The Mac in this TSIG
163    pub mac: Vec<u8>,
164
165    /// The original ID
166    pub oid: u16,
167
168    /// The TSIG error RCODE
169    ///
170    /// This is separate from the top-level error RCODE of a response
171    /// See <https://www.rfc-editor.org/rfc/rfc8945.html#section-3>
172    pub error: Option<TsigError>,
173
174    /// Additional data relevant to this TSIG
175    pub other: Vec<u8>,
176}
177
178impl TSIG {
179    #[cfg(feature = "__dnssec")]
180    pub(crate) fn stub(oid: u16, time: u64, signer: &TSigner) -> Self {
181        TSIG::new(
182            signer.algorithm().clone(),
183            time,
184            signer.fudge(),
185            Vec::new(),
186            oid,
187            None,
188            Vec::new(),
189        )
190    }
191
192    /// Constructs a new TSIG
193    ///
194    /// [RFC 8945, Secret Key Transaction Authentication for DNS](https://tools.ietf.org/html/rfc8945#section-4.1)
195    ///
196    /// ```text
197    /// 4.1.  TSIG RR Type
198    ///
199    ///   To provide secret key authentication, we use an RR type whose
200    ///   mnemonic is TSIG and whose type code is 250.  TSIG is a meta-RR and
201    ///   MUST NOT be cached.  TSIG RRs are used for authentication between DNS
202    ///   entities that have established a shared secret key.  TSIG RRs are
203    ///   dynamically computed to cover a particular DNS transaction and are
204    ///   not DNS RRs in the usual sense.
205    ///
206    ///   As the TSIG RRs are related to one DNS request/response, there is no
207    ///   value in storing or retransmitting them; thus, the TSIG RR is
208    ///   discarded once it has been used to authenticate a DNS message.
209    /// ```
210    pub fn new(
211        algorithm: TsigAlgorithm,
212        time: u64,
213        fudge: u16,
214        mac: Vec<u8>,
215        oid: u16,
216        error: Option<TsigError>,
217        other: Vec<u8>,
218    ) -> Self {
219        Self {
220            algorithm,
221            time,
222            fudge,
223            mac,
224            oid,
225            error,
226            other,
227        }
228    }
229
230    /// Emit TSIG RR and RDATA as used for computing MAC
231    ///
232    /// ```text
233    /// 4.3.3.  TSIG Variables
234    ///
235    ///    Also included in the digest is certain information present in the
236    ///    TSIG RR.  Adding this data provides further protection against an
237    ///    attempt to interfere with the message.
238    ///
239    ///    +============+================+====================================+
240    ///    | Source     | Field Name     | Notes                              |
241    ///    +============+================+====================================+
242    ///    | TSIG RR    | NAME           | Key name, in canonical wire format |
243    ///    +------------+----------------+------------------------------------+
244    ///    | TSIG RR    | CLASS          | MUST be ANY                        |
245    ///    +------------+----------------+------------------------------------+
246    ///    | TSIG RR    | TTL            | MUST be 0                          |
247    ///    +------------+----------------+------------------------------------+
248    ///    | TSIG RDATA | Algorithm Name | in canonical wire format           |
249    ///    +------------+----------------+------------------------------------+
250    ///    | TSIG RDATA | Time Signed    | in network byte order              |
251    ///    +------------+----------------+------------------------------------+
252    ///    | TSIG RDATA | Fudge          | in network byte order              |
253    ///    +------------+----------------+------------------------------------+
254    ///    | TSIG RDATA | Error          | in network byte order              |
255    ///    +------------+----------------+------------------------------------+
256    ///    | TSIG RDATA | Other Len      | in network byte order              |
257    ///    +------------+----------------+------------------------------------+
258    ///    | TSIG RDATA | Other Data     | exactly as transmitted             |
259    ///    +------------+----------------+------------------------------------+
260    /// ```
261    pub fn emit_tsig_for_mac(
262        &self,
263        encoder: &mut BinEncoder<'_>,
264        key_name: &Name,
265    ) -> ProtoResult<()> {
266        let mut encoder = encoder.with_name_encoding(NameEncoding::UncompressedLowercase);
267
268        key_name.emit(&mut encoder)?;
269        DNSClass::ANY.emit(&mut encoder)?;
270        encoder.emit_u32(0)?; // TTL
271        self.algorithm.emit(&mut encoder)?;
272        encoder.emit_u16((self.time >> 32) as u16)?;
273        encoder.emit_u32(self.time as u32)?;
274        encoder.emit_u16(self.fudge)?;
275        encoder.emit_u16(match self.error {
276            None => 0,
277            Some(err) => u16::from(err),
278        })?;
279        encoder.emit_u16(self.other.len() as u16)?;
280        encoder.emit_vec(&self.other)?;
281        Ok(())
282    }
283
284    /// Add actual MAC value to existing TSIG record data.
285    ///
286    /// # Arguments
287    ///
288    /// * `mac` - mac to be stored in this record.
289    pub fn set_mac(self, mac: Vec<u8>) -> Self {
290        Self { mac, ..self }
291    }
292}
293
294impl BinEncodable for TSIG {
295    /// Write the RData from the given Encoder
296    ///
297    /// ```text
298    ///                       1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
299    ///   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
300    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
301    ///  /                         Algorithm Name                        /
302    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
303    ///  |                                                               |
304    ///  |          Time Signed          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
305    ///  |                               |            Fudge              |
306    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307    ///  |          MAC Size             |                               /
308    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+             MAC               /
309    ///  /                                                               /
310    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311    ///  |          Original ID          |            Error              |
312    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
313    ///  |          Other Len            |                               /
314    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+           Other Data          /
315    ///  /                                                               /
316    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
317    /// ```
318    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
319        let mut encoder = encoder.with_rdata_behavior(RDataEncoding::Other);
320        self.algorithm.emit(&mut encoder)?;
321        encoder.emit_u16(
322            (self.time >> 32)
323                .try_into()
324                .map_err(|_| ProtoError::from("invalid time, overflow 48 bit counter in TSIG"))?,
325        )?;
326        encoder.emit_u32(self.time as u32)?; // this cast is supposed to truncate
327        encoder.emit_u16(self.fudge)?;
328        encoder.emit_u16(
329            self.mac
330                .len()
331                .try_into()
332                .map_err(|_| ProtoError::from("invalid mac, longer than 65535 B in TSIG"))?,
333        )?;
334        encoder.emit_vec(&self.mac)?;
335        encoder.emit_u16(self.oid)?;
336        encoder.emit_u16(match self.error {
337            None => 0,
338            Some(err) => u16::from(err),
339        })?;
340        encoder.emit_u16(self.other.len().try_into().map_err(|_| {
341            ProtoError::from("invalid other_buffer, longer than 65535 B in TSIG")
342        })?)?;
343        encoder.emit_vec(&self.other)?;
344        Ok(())
345    }
346}
347
348impl<'r> RecordDataDecodable<'r> for TSIG {
349    /// Read the RData from the given Decoder
350    ///
351    /// ```text
352    ///                       1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
353    ///   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
354    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
355    ///  /                         Algorithm Name                        /
356    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
357    ///  |                                                               |
358    ///  |          Time Signed          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
359    ///  |                               |            Fudge              |
360    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
361    ///  |          MAC Size             |                               /
362    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+             MAC               /
363    ///  /                                                               /
364    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365    ///  |          Original ID          |            Error              |
366    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367    ///  |          Other Len            |                               /
368    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+           Other Data          /
369    ///  /                                                               /
370    ///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371    /// ```
372    fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
373        let end_idx = length.map(|rdl| rdl as usize)
374        .checked_add(decoder.index())
375        .map_err(|len| DecodeError::IncorrectRDataLengthRead { read: decoder.index(), len })? // no legal message is long enough to trigger that
376        .unverified(/*used only as length safely*/);
377
378        let algorithm = TsigAlgorithm::read(decoder)?;
379        let time_high = decoder.read_u16()?.unverified(/*valid as any u16*/) as u64;
380        let time_low = decoder.read_u32()?.unverified(/*valid as any u32*/) as u64;
381        let time = (time_high << 32) | time_low;
382        let fudge = decoder.read_u16()?.unverified(/*valid as any u16*/);
383        let mac_size = decoder
384            .read_u16()?
385            .verify_unwrap(|&size| decoder.index() + size as usize + 6 /* 3 u16 */ <= end_idx)
386            .map_err(|size| DecodeError::IncorrectRDataLengthRead {
387                read: end_idx - decoder.index(),
388                len: size as usize + 6,
389            })?;
390        let mac =
391            decoder.read_vec(mac_size as usize)?.unverified(/*valid as any vec of the right size*/);
392        let oid = decoder.read_u16()?.unverified(/*valid as any u16*/);
393        let error = match decoder.read_u16()?.unverified(/*valid as any u16*/) {
394            0 => None,
395            code => Some(TsigError::from(code)),
396        };
397        let other_len = decoder
398            .read_u16()?
399            .verify_unwrap(|&size| decoder.index() + size as usize == end_idx)
400            .map_err(|size| DecodeError::IncorrectRDataLengthRead {
401                read: end_idx - decoder.index(),
402                len: size as usize,
403            })?;
404        let other = decoder.read_vec(other_len as usize)?.unverified(/*valid as any vec of the right size*/);
405
406        Ok(Self {
407            algorithm,
408            time,
409            fudge,
410            mac,
411            oid,
412            error,
413            other,
414        })
415    }
416}
417
418impl RecordData for TSIG {
419    fn try_borrow(data: &RData) -> Option<&Self> {
420        match data {
421            RData::TSIG(csync) => Some(csync),
422            _ => None,
423        }
424    }
425
426    fn record_type(&self) -> RecordType {
427        RecordType::TSIG
428    }
429
430    fn into_rdata(self) -> RData {
431        RData::TSIG(self)
432    }
433}
434
435// Does not appear to have a normalized text representation
436impl fmt::Display for TSIG {
437    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
438        write!(
439            f,
440            "{algorithm} {time} {fudge} {mac} {oid} {error} {other}",
441            algorithm = self.algorithm,
442            time = self.time,
443            fudge = self.fudge,
444            mac = sshfp::HEX.encode(&self.mac),
445            oid = self.oid,
446            error = self.error.map(Into::into).unwrap_or(0),
447            other = sshfp::HEX.encode(&self.other),
448        )
449    }
450}
451
452/// Algorithm used to authenticate communication
453///
454/// [RFC8945 Secret Key Transaction Authentication for DNS](https://tools.ietf.org/html/rfc8945#section-6)
455/// ```text
456///      +==========================+================+=================+
457///      | Algorithm Name           | Implementation | Use             |
458///      +==========================+================+=================+
459///      | HMAC-MD5.SIG-ALG.REG.INT | MAY            | MUST NOT        |
460///      +--------------------------+----------------+-----------------+
461///      | gss-tsig                 | MAY            | MAY             |
462///      +--------------------------+----------------+-----------------+
463///      | hmac-sha1                | MUST           | NOT RECOMMENDED |
464///      +--------------------------+----------------+-----------------+
465///      | hmac-sha224              | MAY            | MAY             |
466///      +--------------------------+----------------+-----------------+
467///      | hmac-sha256              | MUST           | RECOMMENDED     |
468///      +--------------------------+----------------+-----------------+
469///      | hmac-sha256-128          | MAY            | MAY             |
470///      +--------------------------+----------------+-----------------+
471///      | hmac-sha384              | MAY            | MAY             |
472///      +--------------------------+----------------+-----------------+
473///      | hmac-sha384-192          | MAY            | MAY             |
474///      +--------------------------+----------------+-----------------+
475///      | hmac-sha512              | MAY            | MAY             |
476///      +--------------------------+----------------+-----------------+
477///      | hmac-sha512-256          | MAY            | MAY             |
478///      +--------------------------+----------------+-----------------+
479/// ```
480#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
481#[derive(Debug, PartialEq, Eq, Hash, Clone)]
482pub enum TsigAlgorithm {
483    /// HMAC-MD5.SIG-ALG.REG.INT (not supported for cryptographic operations)
484    #[cfg_attr(feature = "serde", serde(rename = "HMAC-MD5.SIG-ALG.REG.INT"))]
485    HmacMd5,
486    /// gss-tsig (not supported for cryptographic operations)
487    #[cfg_attr(feature = "serde", serde(rename = "gss-tsig"))]
488    Gss,
489    /// hmac-sha1 (not supported for cryptographic operations)
490    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha1"))]
491    HmacSha1,
492    /// hmac-sha224 (not supported for cryptographic operations)
493    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha224"))]
494    HmacSha224,
495    /// hmac-sha256
496    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha256"))]
497    HmacSha256,
498    /// hmac-sha256-128 (not supported for cryptographic operations)
499    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha256-128"))]
500    HmacSha256_128,
501    /// hmac-sha384
502    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha384"))]
503    HmacSha384,
504    /// hmac-sha384-192 (not supported for cryptographic operations)
505    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha384-192"))]
506    HmacSha384_192,
507    /// hmac-sha512
508    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha512"))]
509    HmacSha512,
510    /// hmac-sha512-256 (not supported for cryptographic operations)
511    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha512-256"))]
512    HmacSha512_256,
513    /// Unknown algorithm
514    Unknown(Name),
515}
516
517impl TsigAlgorithm {
518    /// Return DNS name for the algorithm
519    pub fn to_name(&self) -> Name {
520        use TsigAlgorithm::*;
521        match self {
522            HmacMd5 => Name::from_ascii("HMAC-MD5.SIG-ALG.REG.INT"),
523            Gss => Name::from_ascii("gss-tsig"),
524            HmacSha1 => Name::from_ascii("hmac-sha1"),
525            HmacSha224 => Name::from_ascii("hmac-sha224"),
526            HmacSha256 => Name::from_ascii("hmac-sha256"),
527            HmacSha256_128 => Name::from_ascii("hmac-sha256-128"),
528            HmacSha384 => Name::from_ascii("hmac-sha384"),
529            HmacSha384_192 => Name::from_ascii("hmac-sha384-192"),
530            HmacSha512 => Name::from_ascii("hmac-sha512"),
531            HmacSha512_256 => Name::from_ascii("hmac-sha512-256"),
532            Unknown(name) => Ok(name.clone()),
533        }.unwrap(/* should not fail with static strings*/)
534    }
535
536    /// Convert a DNS name to an Algorithm
537    pub fn from_name(name: Name) -> Self {
538        use TsigAlgorithm::*;
539        match name.to_ascii().as_str() {
540            "HMAC-MD5.SIG-ALG.REG.INT" => HmacMd5,
541            "gss-tsig" => Gss,
542            "hmac-sha1" => HmacSha1,
543            "hmac-sha224" => HmacSha224,
544            "hmac-sha256" => HmacSha256,
545            "hmac-sha256-128" => HmacSha256_128,
546            "hmac-sha384" => HmacSha384,
547            "hmac-sha384-192" => HmacSha384_192,
548            "hmac-sha512" => HmacSha512,
549            "hmac-sha512-256" => HmacSha512_256,
550            _ => Unknown(name),
551        }
552    }
553
554    /// Compute the Message Authentication Code using key and algorithm
555    ///
556    /// Supported algorithm are HmacSha256, HmacSha384, HmacSha512 and HmacSha512_256
557    /// Other algorithm return an error.
558    #[cfg(feature = "__dnssec")]
559    pub fn mac_data(&self, key: &[u8], message: &[u8]) -> Result<Vec<u8>, DnsSecError> {
560        use TsigAlgorithm::*;
561
562        let key = match self {
563            HmacSha256 => hmac::Key::new(hmac::HMAC_SHA256, key),
564            HmacSha384 => hmac::Key::new(hmac::HMAC_SHA384, key),
565            HmacSha512 => hmac::Key::new(hmac::HMAC_SHA512, key),
566            _ => return Err(DnsSecError::TsigUnsupportedMacAlgorithm(self.clone())),
567        };
568
569        let mac = hmac::sign(&key, message);
570        let res = mac.as_ref().to_vec();
571
572        Ok(res)
573    }
574
575    /// Verifies the hmac tag against the given key and this algorithm.
576    ///
577    /// This is both faster than independently creating the MAC and also constant time preventing timing attacks
578    #[cfg(feature = "__dnssec")]
579    pub fn verify_mac(&self, key: &[u8], message: &[u8], tag: &[u8]) -> Result<(), DnsSecError> {
580        use TsigAlgorithm::*;
581
582        let key = match self {
583            HmacSha256 => hmac::Key::new(hmac::HMAC_SHA256, key),
584            HmacSha384 => hmac::Key::new(hmac::HMAC_SHA384, key),
585            HmacSha512 => hmac::Key::new(hmac::HMAC_SHA512, key),
586            _ => return Err(DnsSecError::TsigUnsupportedMacAlgorithm(self.clone())),
587        };
588
589        hmac::verify(&key, message, tag).map_err(|_| DnsSecError::HmacInvalid)
590    }
591
592    /// Return `true` if cryptographic operations needed for using this algorithm are supported,
593    /// `false` otherwise
594    ///
595    /// ## Supported
596    ///
597    /// - HmacSha256
598    /// - HmacSha384
599    /// - HmacSha512
600    /// - HmacSha512_256
601    pub fn supported(&self) -> bool {
602        use TsigAlgorithm::*;
603        matches!(self, HmacSha256 | HmacSha384 | HmacSha512)
604    }
605
606    /// Return length in bytes of the algorithms output
607    #[cfg(feature = "__dnssec")]
608    pub(crate) fn output_len(&self) -> Result<usize, DnsSecError> {
609        use TsigAlgorithm::*;
610
611        let len = match self {
612            HmacSha256 => hmac::HMAC_SHA256.digest_algorithm().output_len(),
613            HmacSha384 => hmac::HMAC_SHA384.digest_algorithm().output_len(),
614            HmacSha512 => hmac::HMAC_SHA512.digest_algorithm().output_len(),
615            _ => return Err(DnsSecError::TsigUnsupportedMacAlgorithm(self.clone())),
616        };
617
618        Ok(len)
619    }
620}
621
622impl fmt::Display for TsigAlgorithm {
623    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
624        write!(f, "{}", self.to_name())
625    }
626}
627
628impl BinEncodable for TsigAlgorithm {
629    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
630        self.to_name().emit(encoder)
631    }
632}
633
634impl BinDecodable<'_> for TsigAlgorithm {
635    fn read(decoder: &mut BinDecoder<'_>) -> Result<Self, DecodeError> {
636        let mut name = Name::read(decoder)?;
637        name.set_fqdn(false);
638        Ok(Self::from_name(name))
639    }
640}
641
642/// A TSIG RR error rcode
643///
644/// See <https://www.rfc-editor.org/rfc/rfc8945.html#section-3>
645#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
646#[derive(Debug, Eq, PartialEq, PartialOrd, Copy, Clone, Hash)]
647pub enum TsigError {
648    /// Bad signature
649    BadSig,
650    /// Bad key
651    BadKey,
652    /// Bad signature time
653    BadTime,
654    /// Bad truncated request MAC
655    BadTrunc,
656    /// An unknown error
657    Unknown(u16),
658}
659
660impl From<u16> for TsigError {
661    fn from(value: u16) -> Self {
662        match value {
663            16 => Self::BadSig,
664            17 => Self::BadKey,
665            18 => Self::BadTime,
666            22 => Self::BadTrunc,
667            code => Self::Unknown(code),
668        }
669    }
670}
671
672impl From<TsigError> for u16 {
673    fn from(value: TsigError) -> Self {
674        match value {
675            TsigError::BadSig => 16,
676            TsigError::BadKey => 17,
677            TsigError::BadTime => 18,
678            TsigError::BadTrunc => 22,
679            TsigError::Unknown(code) => code,
680        }
681    }
682}
683
684/// Return the to-be-signed data for authenticating the message with TSIG.
685///
686/// # Arguments
687///
688/// * `message` - the message to authenticate. Should not be modified after calling this function
689///   except to add the final TSIG record
690/// * `pre_tsig` - TSIG rrdata, possibly with missing MAC. Should not be modified in any other way
691///   after calling this function.
692/// * `key_name` - the name of the TSIG key, should be the same as the name known by the remote
693///   peer.
694pub fn message_tbs<M: BinEncodable>(
695    message: &M,
696    pre_tsig: &TSIG,
697    key_name: &Name,
698) -> ProtoResult<Vec<u8>> {
699    let mut buf = Vec::with_capacity(512);
700    let mut encoder = BinEncoder::new(&mut buf);
701    message.emit(&mut encoder)?;
702    pre_tsig.emit_tsig_for_mac(&mut encoder, key_name)?;
703    Ok(buf)
704}
705
706/// Return the byte-message that would have been used to generate a TSIG
707///
708/// # Arguments
709///
710/// * `previous_hash` - hash of previous message in case of message chaining, or of query in case
711///   of response. Should be None for query
712/// * `message` - the byte-message to authenticate, with included TSIG
713/// * `first_message` - whether to emit the tsig pseudo-record for a first message
714#[cfg(feature = "__dnssec")]
715pub fn signed_bitmessage_to_buf(
716    message: &[u8],
717    previous_hash: Option<&[u8]>,
718    first_message: bool,
719) -> ProtoResult<(Vec<u8>, Box<Record<TSIG>>)> {
720    let mut decoder = BinDecoder::new(message);
721    let Header {
722        mut metadata,
723        mut counts,
724    } = Header::read(&mut decoder)?;
725
726    // Adjust the header additional count down by one - this separates out the final
727    // additional data TSIG record.
728    if counts.additionals > 0 {
729        counts.additionals -= 1;
730    } else {
731        return Err(ProtoError::from(
732            "missing tsig from response that must be authenticated",
733        ));
734    }
735
736    // Note the position of the decoder in the message, past the header, before reading any data.
737    let start_data = message.len() - decoder.len();
738
739    // Advance past the queries.
740    let count = counts.queries;
741    for _ in 0..count {
742        Query::read(&mut decoder)?;
743    }
744
745    // Advance past answer and authority records together.
746    let answer_authority_count = (counts.answers + counts.authorities) as usize;
747    let (_, _, sig) = Message::read_records(
748        &mut decoder,
749        answer_authority_count,
750        false,
751        metadata.op_code,
752    )?;
753    debug_assert!(sig.is_none());
754
755    // Advance past additional records, up to the final TSIG record.
756    let (_, _, sig) = Message::read_records(
757        &mut decoder,
758        counts.additionals as usize,
759        true,
760        metadata.op_code,
761    )?;
762    debug_assert!(sig.is_none());
763    // Note the position of the decoder ahead of the final additional data TSIG record.
764    let end_data = message.len() - decoder.len();
765
766    // Read the TSIG signature record.
767    let (_, _, sig) = Message::read_records(&mut decoder, 1, true, metadata.op_code)?;
768    let Some(tsig_rr) = sig else {
769        return Err(ProtoError::from("TSIG signature record not found"));
770    };
771
772    let tsig = &tsig_rr.data;
773    metadata.id = tsig.oid;
774
775    // Construct the TBS data.
776    let mut buf = Vec::with_capacity(message.len());
777    let mut encoder = BinEncoder::new(&mut buf);
778
779    // Prepend the previous hash if provided.
780    if let Some(previous_hash) = previous_hash {
781        encoder.emit_u16(previous_hash.len() as u16)?;
782        encoder.emit_vec(previous_hash)?;
783    }
784
785    // Emit the header we modified to remove the TSIG additional record.
786    Header { metadata, counts }.emit(&mut encoder)?;
787
788    // Emit all the message data between the header and the TSIG record.
789    encoder.emit_vec(&message[start_data..end_data])?;
790
791    if first_message {
792        // Emit the TSIG pseudo-record when this is the first message.
793        tsig.emit_tsig_for_mac(&mut encoder, &tsig_rr.name)?;
794    } else {
795        // Emit only time and fudge data for later messages.
796        encoder.emit_u16((tsig.time >> 32) as u16)?;
797        encoder.emit_u32(tsig.time as u32)?;
798        encoder.emit_u16(tsig.fudge)?;
799    }
800
801    Ok((buf, tsig_rr))
802}
803
804/// Helper function to make a TSIG record from the name of the key, and the TSIG RData
805pub fn make_tsig_record(name: Name, rdata: TSIG) -> Record<TSIG> {
806    // https://tools.ietf.org/html/rfc8945#section-4.2
807
808    let mut tsig = Record::from_rdata(
809        name,  //   NAME:  The name of the key used, in domain name syntax
810        0,     //   TTL:  This MUST be 0.
811        rdata, //   TYPE:  This MUST be TSIG (250: Transaction SIGnature).
812    );
813
814    //   CLASS:  This MUST be ANY.
815    tsig.dns_class = DNSClass::ANY;
816    tsig
817}
818
819#[cfg(test)]
820mod tests {
821    #![allow(clippy::dbg_macro, clippy::print_stdout)]
822
823    use std::println;
824
825    use super::*;
826    #[cfg(feature = "__dnssec")]
827    use crate::rr::Record;
828
829    fn test_encode_decode(rdata: TSIG) {
830        let mut bytes = Vec::new();
831        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
832        rdata.emit(&mut encoder).expect("failed to emit tsig");
833        let bytes = encoder.into_bytes();
834
835        println!("bytes: {bytes:?}");
836
837        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
838        let read_rdata = TSIG::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
839            .expect("failed to read back");
840        assert_eq!(rdata, read_rdata);
841    }
842
843    #[test]
844    fn test_encode_decode_tsig() {
845        test_encode_decode(TSIG::new(
846            TsigAlgorithm::HmacSha256,
847            0,
848            300,
849            vec![0, 1, 2, 3],
850            0,
851            None,
852            vec![4, 5, 6, 7],
853        ));
854        test_encode_decode(TSIG::new(
855            TsigAlgorithm::HmacSha384,
856            123456789,
857            60,
858            vec![9, 8, 7, 6, 5, 4],
859            1,
860            Some(TsigError::BadKey),
861            vec![],
862        ));
863        test_encode_decode(TSIG::new(
864            TsigAlgorithm::Unknown(Name::from_ascii("unknown_algorithm").unwrap()),
865            123456789,
866            60,
867            vec![],
868            1,
869            Some(TsigError::BadTime),
870            vec![0, 1, 2, 3, 4, 5, 6],
871        ));
872        test_encode_decode(TSIG::new(
873            TsigAlgorithm::Unknown(Name::from_ascii("unknown_algorithm").unwrap()),
874            123456789,
875            60,
876            vec![],
877            1,
878            Some(TsigError::Unknown(420)),
879            vec![0, 1, 2, 3, 4, 5, 6],
880        ));
881    }
882
883    #[test]
884    #[cfg(feature = "__dnssec")]
885    fn test_sign_encode() {
886        let mut message = Message::query();
887        message.add_answer(Record::stub());
888
889        let key_name = Name::from_ascii("some.name").unwrap();
890
891        let pre_tsig = TSIG::new(
892            TsigAlgorithm::HmacSha256,
893            12345,
894            60,
895            vec![],
896            message.id,
897            None,
898            vec![],
899        );
900
901        let tbs = message_tbs(&message, &pre_tsig, &key_name).unwrap();
902
903        let pre_tsig = pre_tsig.set_mac(b"some signature".to_vec());
904
905        message.set_signature(Box::new(make_tsig_record(key_name, pre_tsig)));
906
907        let message_byte = message.to_bytes().unwrap();
908
909        let tbv = signed_bitmessage_to_buf(&message_byte, None, true)
910            .unwrap()
911            .0;
912
913        assert_eq!(tbs, tbv);
914    }
915
916    #[test]
917    #[cfg(feature = "__dnssec")]
918    fn test_sign_encode_id_changed() {
919        let mut message = Message::query();
920        message.metadata.id = 123;
921        message.answers.push(Record::stub());
922
923        let key_name = Name::from_ascii("some.name").unwrap();
924
925        let pre_tsig = TSIG::new(
926            TsigAlgorithm::HmacSha256,
927            12345,
928            60,
929            vec![],
930            message.id,
931            None,
932            vec![],
933        );
934
935        let tbs = message_tbs(&message, &pre_tsig, &key_name).unwrap();
936
937        let pre_tsig = pre_tsig.set_mac(b"some signature".to_vec());
938
939        message.set_signature(Box::new(make_tsig_record(key_name, pre_tsig)));
940
941        let message_byte = message.to_bytes().unwrap();
942        let mut message = Message::from_bytes(&message_byte).unwrap();
943
944        message.metadata.id = 456; // simulate the request id being changed due to request forwarding
945
946        let message_byte = message.to_bytes().unwrap();
947
948        let tbv = signed_bitmessage_to_buf(&message_byte, None, true)
949            .unwrap()
950            .0;
951
952        assert_eq!(tbs, tbv);
953
954        // sign and verify
955        let key = &[0, 1, 2, 3, 4];
956
957        let tag = TsigAlgorithm::HmacSha256.mac_data(key, &tbv).unwrap();
958
959        TsigAlgorithm::HmacSha256
960            .verify_mac(key, &tbv, &tag)
961            .expect("did not verify")
962    }
963}