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(|_| DecodeError::InsufficientBytes)?;
387        let mac =
388            decoder.read_vec(mac_size as usize)?.unverified(/*valid as any vec of the right size*/);
389        let oid = decoder.read_u16()?.unverified(/*valid as any u16*/);
390        let error = match decoder.read_u16()?.unverified(/*valid as any u16*/) {
391            0 => None,
392            code => Some(TsigError::from(code)),
393        };
394        let other_len = decoder
395            .read_u16()?
396            .verify_unwrap(|&size| decoder.index() + size as usize == end_idx)
397            .map_err(|_| DecodeError::InsufficientBytes)?;
398        let other = decoder.read_vec(other_len as usize)?.unverified(/*valid as any vec of the right size*/);
399
400        Ok(Self {
401            algorithm,
402            time,
403            fudge,
404            mac,
405            oid,
406            error,
407            other,
408        })
409    }
410}
411
412impl RecordData for TSIG {
413    fn try_borrow(data: &RData) -> Option<&Self> {
414        match data {
415            RData::TSIG(csync) => Some(csync),
416            _ => None,
417        }
418    }
419
420    fn record_type(&self) -> RecordType {
421        RecordType::TSIG
422    }
423
424    fn into_rdata(self) -> RData {
425        RData::TSIG(self)
426    }
427}
428
429// Does not appear to have a normalized text representation
430impl fmt::Display for TSIG {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
432        write!(
433            f,
434            "{algorithm} {time} {fudge} {mac} {oid} {error} {other}",
435            algorithm = self.algorithm,
436            time = self.time,
437            fudge = self.fudge,
438            mac = sshfp::HEX.encode(&self.mac),
439            oid = self.oid,
440            error = self.error.map(Into::into).unwrap_or(0),
441            other = sshfp::HEX.encode(&self.other),
442        )
443    }
444}
445
446/// Algorithm used to authenticate communication
447///
448/// [RFC8945 Secret Key Transaction Authentication for DNS](https://tools.ietf.org/html/rfc8945#section-6)
449/// ```text
450///      +==========================+================+=================+
451///      | Algorithm Name           | Implementation | Use             |
452///      +==========================+================+=================+
453///      | HMAC-MD5.SIG-ALG.REG.INT | MAY            | MUST NOT        |
454///      +--------------------------+----------------+-----------------+
455///      | gss-tsig                 | MAY            | MAY             |
456///      +--------------------------+----------------+-----------------+
457///      | hmac-sha1                | MUST           | NOT RECOMMENDED |
458///      +--------------------------+----------------+-----------------+
459///      | hmac-sha224              | MAY            | MAY             |
460///      +--------------------------+----------------+-----------------+
461///      | hmac-sha256              | MUST           | RECOMMENDED     |
462///      +--------------------------+----------------+-----------------+
463///      | hmac-sha256-128          | MAY            | MAY             |
464///      +--------------------------+----------------+-----------------+
465///      | hmac-sha384              | MAY            | MAY             |
466///      +--------------------------+----------------+-----------------+
467///      | hmac-sha384-192          | MAY            | MAY             |
468///      +--------------------------+----------------+-----------------+
469///      | hmac-sha512              | MAY            | MAY             |
470///      +--------------------------+----------------+-----------------+
471///      | hmac-sha512-256          | MAY            | MAY             |
472///      +--------------------------+----------------+-----------------+
473/// ```
474#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
475#[derive(Debug, PartialEq, Eq, Hash, Clone)]
476pub enum TsigAlgorithm {
477    /// HMAC-MD5.SIG-ALG.REG.INT (not supported for cryptographic operations)
478    #[cfg_attr(feature = "serde", serde(rename = "HMAC-MD5.SIG-ALG.REG.INT"))]
479    HmacMd5,
480    /// gss-tsig (not supported for cryptographic operations)
481    #[cfg_attr(feature = "serde", serde(rename = "gss-tsig"))]
482    Gss,
483    /// hmac-sha1 (not supported for cryptographic operations)
484    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha1"))]
485    HmacSha1,
486    /// hmac-sha224 (not supported for cryptographic operations)
487    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha224"))]
488    HmacSha224,
489    /// hmac-sha256
490    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha256"))]
491    HmacSha256,
492    /// hmac-sha256-128 (not supported for cryptographic operations)
493    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha256-128"))]
494    HmacSha256_128,
495    /// hmac-sha384
496    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha384"))]
497    HmacSha384,
498    /// hmac-sha384-192 (not supported for cryptographic operations)
499    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha384-192"))]
500    HmacSha384_192,
501    /// hmac-sha512
502    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha512"))]
503    HmacSha512,
504    /// hmac-sha512-256 (not supported for cryptographic operations)
505    #[cfg_attr(feature = "serde", serde(rename = "hmac-sha512-256"))]
506    HmacSha512_256,
507    /// Unknown algorithm
508    Unknown(Name),
509}
510
511impl TsigAlgorithm {
512    /// Return DNS name for the algorithm
513    pub fn to_name(&self) -> Name {
514        use TsigAlgorithm::*;
515        match self {
516            HmacMd5 => Name::from_ascii("HMAC-MD5.SIG-ALG.REG.INT"),
517            Gss => Name::from_ascii("gss-tsig"),
518            HmacSha1 => Name::from_ascii("hmac-sha1"),
519            HmacSha224 => Name::from_ascii("hmac-sha224"),
520            HmacSha256 => Name::from_ascii("hmac-sha256"),
521            HmacSha256_128 => Name::from_ascii("hmac-sha256-128"),
522            HmacSha384 => Name::from_ascii("hmac-sha384"),
523            HmacSha384_192 => Name::from_ascii("hmac-sha384-192"),
524            HmacSha512 => Name::from_ascii("hmac-sha512"),
525            HmacSha512_256 => Name::from_ascii("hmac-sha512-256"),
526            Unknown(name) => Ok(name.clone()),
527        }.unwrap(/* should not fail with static strings*/)
528    }
529
530    /// Convert a DNS name to an Algorithm
531    pub fn from_name(name: Name) -> Self {
532        use TsigAlgorithm::*;
533        match name.to_ascii().as_str() {
534            "HMAC-MD5.SIG-ALG.REG.INT" => HmacMd5,
535            "gss-tsig" => Gss,
536            "hmac-sha1" => HmacSha1,
537            "hmac-sha224" => HmacSha224,
538            "hmac-sha256" => HmacSha256,
539            "hmac-sha256-128" => HmacSha256_128,
540            "hmac-sha384" => HmacSha384,
541            "hmac-sha384-192" => HmacSha384_192,
542            "hmac-sha512" => HmacSha512,
543            "hmac-sha512-256" => HmacSha512_256,
544            _ => Unknown(name),
545        }
546    }
547
548    /// Compute the Message Authentication Code using key and algorithm
549    ///
550    /// Supported algorithm are HmacSha256, HmacSha384, HmacSha512 and HmacSha512_256
551    /// Other algorithm return an error.
552    #[cfg(feature = "__dnssec")]
553    pub fn mac_data(&self, key: &[u8], message: &[u8]) -> Result<Vec<u8>, DnsSecError> {
554        use TsigAlgorithm::*;
555
556        let key = match self {
557            HmacSha256 => hmac::Key::new(hmac::HMAC_SHA256, key),
558            HmacSha384 => hmac::Key::new(hmac::HMAC_SHA384, key),
559            HmacSha512 => hmac::Key::new(hmac::HMAC_SHA512, key),
560            _ => return Err(DnsSecError::TsigUnsupportedMacAlgorithm(self.clone())),
561        };
562
563        let mac = hmac::sign(&key, message);
564        let res = mac.as_ref().to_vec();
565
566        Ok(res)
567    }
568
569    /// Verifies the hmac tag against the given key and this algorithm.
570    ///
571    /// This is both faster than independently creating the MAC and also constant time preventing timing attacks
572    #[cfg(feature = "__dnssec")]
573    pub fn verify_mac(&self, key: &[u8], message: &[u8], tag: &[u8]) -> Result<(), DnsSecError> {
574        use TsigAlgorithm::*;
575
576        let key = match self {
577            HmacSha256 => hmac::Key::new(hmac::HMAC_SHA256, key),
578            HmacSha384 => hmac::Key::new(hmac::HMAC_SHA384, key),
579            HmacSha512 => hmac::Key::new(hmac::HMAC_SHA512, key),
580            _ => return Err(DnsSecError::TsigUnsupportedMacAlgorithm(self.clone())),
581        };
582
583        hmac::verify(&key, message, tag).map_err(|_| DnsSecError::HmacInvalid)
584    }
585
586    /// Return `true` if cryptographic operations needed for using this algorithm are supported,
587    /// `false` otherwise
588    ///
589    /// ## Supported
590    ///
591    /// - HmacSha256
592    /// - HmacSha384
593    /// - HmacSha512
594    /// - HmacSha512_256
595    pub fn supported(&self) -> bool {
596        use TsigAlgorithm::*;
597        matches!(self, HmacSha256 | HmacSha384 | HmacSha512)
598    }
599
600    /// Return length in bytes of the algorithms output
601    #[cfg(feature = "__dnssec")]
602    pub(crate) fn output_len(&self) -> Result<usize, DnsSecError> {
603        use TsigAlgorithm::*;
604
605        let len = match self {
606            HmacSha256 => hmac::HMAC_SHA256.digest_algorithm().output_len(),
607            HmacSha384 => hmac::HMAC_SHA384.digest_algorithm().output_len(),
608            HmacSha512 => hmac::HMAC_SHA512.digest_algorithm().output_len(),
609            _ => return Err(DnsSecError::TsigUnsupportedMacAlgorithm(self.clone())),
610        };
611
612        Ok(len)
613    }
614}
615
616impl fmt::Display for TsigAlgorithm {
617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
618        write!(f, "{}", self.to_name())
619    }
620}
621
622impl BinEncodable for TsigAlgorithm {
623    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
624        self.to_name().emit(encoder)
625    }
626}
627
628impl BinDecodable<'_> for TsigAlgorithm {
629    fn read(decoder: &mut BinDecoder<'_>) -> Result<Self, DecodeError> {
630        let mut name = Name::read(decoder)?;
631        name.set_fqdn(false);
632        Ok(Self::from_name(name))
633    }
634}
635
636/// A TSIG RR error rcode
637///
638/// See <https://www.rfc-editor.org/rfc/rfc8945.html#section-3>
639#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
640#[derive(Debug, Eq, PartialEq, PartialOrd, Copy, Clone, Hash)]
641pub enum TsigError {
642    /// Bad signature
643    BadSig,
644    /// Bad key
645    BadKey,
646    /// Bad signature time
647    BadTime,
648    /// Bad truncated request MAC
649    BadTrunc,
650    /// An unknown error
651    Unknown(u16),
652}
653
654impl From<u16> for TsigError {
655    fn from(value: u16) -> Self {
656        match value {
657            16 => Self::BadSig,
658            17 => Self::BadKey,
659            18 => Self::BadTime,
660            22 => Self::BadTrunc,
661            code => Self::Unknown(code),
662        }
663    }
664}
665
666impl From<TsigError> for u16 {
667    fn from(value: TsigError) -> Self {
668        match value {
669            TsigError::BadSig => 16,
670            TsigError::BadKey => 17,
671            TsigError::BadTime => 18,
672            TsigError::BadTrunc => 22,
673            TsigError::Unknown(code) => code,
674        }
675    }
676}
677
678/// Return the to-be-signed data for authenticating the message with TSIG.
679///
680/// # Arguments
681///
682/// * `message` - the message to authenticate. Should not be modified after calling this function
683///   except to add the final TSIG record
684/// * `pre_tsig` - TSIG rrdata, possibly with missing MAC. Should not be modified in any other way
685///   after calling this function.
686/// * `key_name` - the name of the TSIG key, should be the same as the name known by the remote
687///   peer.
688pub fn message_tbs<M: BinEncodable>(
689    message: &M,
690    pre_tsig: &TSIG,
691    key_name: &Name,
692) -> ProtoResult<Vec<u8>> {
693    let mut buf = Vec::with_capacity(512);
694    let mut encoder = BinEncoder::new(&mut buf);
695    message.emit(&mut encoder)?;
696    pre_tsig.emit_tsig_for_mac(&mut encoder, key_name)?;
697    Ok(buf)
698}
699
700/// Return the byte-message that would have been used to generate a TSIG
701///
702/// # Arguments
703///
704/// * `previous_hash` - hash of previous message in case of message chaining, or of query in case
705///   of response. Should be None for query
706/// * `message` - the byte-message to authenticate, with included TSIG
707/// * `first_message` - whether to emit the tsig pseudo-record for a first message
708#[cfg(feature = "__dnssec")]
709pub fn signed_bitmessage_to_buf(
710    message: &[u8],
711    previous_hash: Option<&[u8]>,
712    first_message: bool,
713) -> ProtoResult<(Vec<u8>, Box<Record<TSIG>>)> {
714    let mut decoder = BinDecoder::new(message);
715    let Header {
716        mut metadata,
717        mut counts,
718    } = Header::read(&mut decoder)?;
719
720    // Adjust the header additional count down by one - this separates out the final
721    // additional data TSIG record.
722    if counts.additionals > 0 {
723        counts.additionals -= 1;
724    } else {
725        return Err(ProtoError::from(
726            "missing tsig from response that must be authenticated",
727        ));
728    }
729
730    // Note the position of the decoder in the message, past the header, before reading any data.
731    let start_data = message.len() - decoder.len();
732
733    // Advance past the queries.
734    let count = counts.queries;
735    for _ in 0..count {
736        Query::read(&mut decoder)?;
737    }
738
739    // Advance past answer and authority records together.
740    let answer_authority_count = (counts.answers + counts.authorities) as usize;
741    let (_, _, sig) = Message::read_records(
742        &mut decoder,
743        answer_authority_count,
744        false,
745        metadata.op_code,
746    )?;
747    debug_assert!(sig.is_none());
748
749    // Advance past additional records, up to the final TSIG record.
750    let (_, _, sig) = Message::read_records(
751        &mut decoder,
752        counts.additionals as usize,
753        true,
754        metadata.op_code,
755    )?;
756    debug_assert!(sig.is_none());
757    // Note the position of the decoder ahead of the final additional data TSIG record.
758    let end_data = message.len() - decoder.len();
759
760    // Read the TSIG signature record.
761    let (_, _, sig) = Message::read_records(&mut decoder, 1, true, metadata.op_code)?;
762    let Some(tsig_rr) = sig else {
763        return Err(ProtoError::from("TSIG signature record not found"));
764    };
765
766    let tsig = &tsig_rr.data;
767    metadata.id = tsig.oid;
768
769    // Construct the TBS data.
770    let mut buf = Vec::with_capacity(message.len());
771    let mut encoder = BinEncoder::new(&mut buf);
772
773    // Prepend the previous hash if provided.
774    if let Some(previous_hash) = previous_hash {
775        encoder.emit_u16(previous_hash.len() as u16)?;
776        encoder.emit_vec(previous_hash)?;
777    }
778
779    // Emit the header we modified to remove the TSIG additional record.
780    Header { metadata, counts }.emit(&mut encoder)?;
781
782    // Emit all the message data between the header and the TSIG record.
783    encoder.emit_vec(&message[start_data..end_data])?;
784
785    if first_message {
786        // Emit the TSIG pseudo-record when this is the first message.
787        tsig.emit_tsig_for_mac(&mut encoder, &tsig_rr.name)?;
788    } else {
789        // Emit only time and fudge data for later messages.
790        encoder.emit_u16((tsig.time >> 32) as u16)?;
791        encoder.emit_u32(tsig.time as u32)?;
792        encoder.emit_u16(tsig.fudge)?;
793    }
794
795    Ok((buf, tsig_rr))
796}
797
798/// Helper function to make a TSIG record from the name of the key, and the TSIG RData
799pub fn make_tsig_record(name: Name, rdata: TSIG) -> Record<TSIG> {
800    // https://tools.ietf.org/html/rfc8945#section-4.2
801
802    let mut tsig = Record::from_rdata(
803        name,  //   NAME:  The name of the key used, in domain name syntax
804        0,     //   TTL:  This MUST be 0.
805        rdata, //   TYPE:  This MUST be TSIG (250: Transaction SIGnature).
806    );
807
808    //   CLASS:  This MUST be ANY.
809    tsig.dns_class = DNSClass::ANY;
810    tsig
811}
812
813#[cfg(test)]
814mod tests {
815    #![allow(clippy::dbg_macro, clippy::print_stdout)]
816
817    use std::println;
818
819    use super::*;
820    #[cfg(feature = "__dnssec")]
821    use crate::rr::Record;
822
823    fn test_encode_decode(rdata: TSIG) {
824        let mut bytes = Vec::new();
825        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
826        rdata.emit(&mut encoder).expect("failed to emit tsig");
827        let bytes = encoder.into_bytes();
828
829        println!("bytes: {bytes:?}");
830
831        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
832        let read_rdata = TSIG::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
833            .expect("failed to read back");
834        assert_eq!(rdata, read_rdata);
835    }
836
837    #[test]
838    fn test_encode_decode_tsig() {
839        test_encode_decode(TSIG::new(
840            TsigAlgorithm::HmacSha256,
841            0,
842            300,
843            vec![0, 1, 2, 3],
844            0,
845            None,
846            vec![4, 5, 6, 7],
847        ));
848        test_encode_decode(TSIG::new(
849            TsigAlgorithm::HmacSha384,
850            123456789,
851            60,
852            vec![9, 8, 7, 6, 5, 4],
853            1,
854            Some(TsigError::BadKey),
855            vec![],
856        ));
857        test_encode_decode(TSIG::new(
858            TsigAlgorithm::Unknown(Name::from_ascii("unknown_algorithm").unwrap()),
859            123456789,
860            60,
861            vec![],
862            1,
863            Some(TsigError::BadTime),
864            vec![0, 1, 2, 3, 4, 5, 6],
865        ));
866        test_encode_decode(TSIG::new(
867            TsigAlgorithm::Unknown(Name::from_ascii("unknown_algorithm").unwrap()),
868            123456789,
869            60,
870            vec![],
871            1,
872            Some(TsigError::Unknown(420)),
873            vec![0, 1, 2, 3, 4, 5, 6],
874        ));
875    }
876
877    #[test]
878    #[cfg(feature = "__dnssec")]
879    fn test_sign_encode() {
880        let mut message = Message::query();
881        message.add_answer(Record::stub());
882
883        let key_name = Name::from_ascii("some.name").unwrap();
884
885        let pre_tsig = TSIG::new(
886            TsigAlgorithm::HmacSha256,
887            12345,
888            60,
889            vec![],
890            message.id,
891            None,
892            vec![],
893        );
894
895        let tbs = message_tbs(&message, &pre_tsig, &key_name).unwrap();
896
897        let pre_tsig = pre_tsig.set_mac(b"some signature".to_vec());
898
899        message.set_signature(Box::new(make_tsig_record(key_name, pre_tsig)));
900
901        let message_byte = message.to_bytes().unwrap();
902
903        let tbv = signed_bitmessage_to_buf(&message_byte, None, true)
904            .unwrap()
905            .0;
906
907        assert_eq!(tbs, tbv);
908    }
909
910    #[test]
911    #[cfg(feature = "__dnssec")]
912    fn test_sign_encode_id_changed() {
913        let mut message = Message::query();
914        message.metadata.id = 123;
915        message.answers.push(Record::stub());
916
917        let key_name = Name::from_ascii("some.name").unwrap();
918
919        let pre_tsig = TSIG::new(
920            TsigAlgorithm::HmacSha256,
921            12345,
922            60,
923            vec![],
924            message.id,
925            None,
926            vec![],
927        );
928
929        let tbs = message_tbs(&message, &pre_tsig, &key_name).unwrap();
930
931        let pre_tsig = pre_tsig.set_mac(b"some signature".to_vec());
932
933        message.set_signature(Box::new(make_tsig_record(key_name, pre_tsig)));
934
935        let message_byte = message.to_bytes().unwrap();
936        let mut message = Message::from_bytes(&message_byte).unwrap();
937
938        message.metadata.id = 456; // simulate the request id being changed due to request forwarding
939
940        let message_byte = message.to_bytes().unwrap();
941
942        let tbv = signed_bitmessage_to_buf(&message_byte, None, true)
943            .unwrap()
944            .0;
945
946        assert_eq!(tbs, tbv);
947
948        // sign and verify
949        let key = &[0, 1, 2, 3, 4];
950
951        let tag = TsigAlgorithm::HmacSha256.mac_data(key, &tbv).unwrap();
952
953        TsigAlgorithm::HmacSha256
954            .verify_mac(key, &tbv, &tag)
955            .expect("did not verify")
956    }
957}