hickory_proto/rr/rdata/
tlsa.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//! TLSA records for storing TLS certificate validation information
9#![allow(clippy::use_self)]
10
11use alloc::vec::Vec;
12use core::fmt;
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17use super::sshfp;
18
19use crate::{
20    error::{ProtoError, ProtoResult},
21    rr::{RData, RecordData, RecordDataDecodable, RecordType},
22    serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict, RestrictedMath},
23};
24
25/// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2.1)
26///
27/// ```text
28/// 2.1.  TLSA RDATA Wire Format
29///
30///    The RDATA for a TLSA RR consists of a one-octet certificate usage
31///    field, a one-octet selector field, a one-octet matching type field,
32///    and the certificate association data field.
33///
34///                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
35///     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
36///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37///    |  Cert. Usage  |   Selector    | Matching Type |               /
38///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               /
39///    /                                                               /
40///    /                 Certificate Association Data                  /
41///    /                                                               /
42///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43/// ```
44#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
45#[derive(Debug, PartialEq, Eq, Hash, Clone)]
46pub struct TLSA {
47    cert_usage: CertUsage,
48    selector: Selector,
49    matching: Matching,
50    cert_data: Vec<u8>,
51}
52
53/// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2.1.1)
54///
55/// ```text
56/// 2.1.1.  The Certificate Usage Field
57///
58///    A one-octet value, called "certificate usage", specifies the provided
59///    association that will be used to match the certificate presented in
60///    the TLS handshake.  This value is defined in a new IANA registry (see
61///    Section 7.2) in order to make it easier to add additional certificate
62///    usages in the future.  The certificate usages defined in this
63///    document are:
64///
65///       0 -- CA
66///
67///       1 -- Service
68///
69///       2 -- TrustAnchor
70///
71///       3 -- DomainIssued
72///
73///    The certificate usages defined in this document explicitly only apply
74///    to PKIX-formatted certificates in DER encoding [X.690].  If TLS
75///    allows other formats later, or if extensions to this RRtype are made
76///    that accept other formats for certificates, those certificates will
77///    need their own certificate usage values.
78/// ```
79///
80/// [RFC 7218, Adding Acronyms to DANE Registries](https://datatracker.ietf.org/doc/html/rfc7218#section-2.1)
81///
82/// ```text
83/// 2.1.  TLSA Certificate Usages Registry
84///
85///   The reference for this registry has been updated to include both
86///   [RFC6698] and this document.
87///
88///    +-------+----------+--------------------------------+-------------+
89///    | Value | Acronym  | Short Description              | Reference   |
90///    +-------+----------+--------------------------------+-------------+
91///    |   0   | PKIX-TA  | CA constraint                  | [RFC6698]   |
92///    |   1   | PKIX-EE  | Service certificate constraint | [RFC6698]   |
93///    |   2   | DANE-TA  | Trust anchor assertion         | [RFC6698]   |
94///    |   3   | DANE-EE  | Domain-issued certificate      | [RFC6698]   |
95///    | 4-254 |          | Unassigned                     |             |
96///    |  255  | PrivCert | Reserved for Private Use       | [RFC6698]   |
97///    +-------+----------+--------------------------------+-------------+
98/// ```
99
100#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
101#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
102pub enum CertUsage {
103    /// ```text
104    ///       0 -- Certificate usage 0 is used to specify a CA certificate, or
105    ///       the public key of such a certificate, that MUST be found in any of
106    ///       the PKIX certification paths for the end entity certificate given
107    ///       by the server in TLS.  This certificate usage is sometimes
108    ///       referred to as "CA constraint" because it limits which CA can be
109    ///       used to issue certificates for a given service on a host.  The
110    ///       presented certificate MUST pass PKIX certification path
111    ///       validation, and a CA certificate that matches the TLSA record MUST
112    ///       be included as part of a valid certification path.  Because this
113    ///       certificate usage allows both trust anchors and CA certificates,
114    ///       the certificate might or might not have the basicConstraints
115    ///       extension present.
116    /// ```
117    #[cfg_attr(feature = "serde", serde(rename = "PKIX-TA"))]
118    PkixTa,
119
120    /// ```text
121    ///       1 -- Certificate usage 1 is used to specify an end entity
122    ///       certificate, or the public key of such a certificate, that MUST be
123    ///       matched with the end entity certificate given by the server in
124    ///       TLS.  This certificate usage is sometimes referred to as "service
125    ///       certificate constraint" because it limits which end entity
126    ///       certificate can be used by a given service on a host.  The target
127    ///       certificate MUST pass PKIX certification path validation and MUST
128    ///       match the TLSA record.
129    /// ```
130    #[cfg_attr(feature = "serde", serde(rename = "PKIX-EE"))]
131    PkixEe,
132
133    /// ```text
134    ///       2 -- Certificate usage 2 is used to specify a certificate, or the
135    ///       public key of such a certificate, that MUST be used as the trust
136    ///       anchor when validating the end entity certificate given by the
137    ///       server in TLS.  This certificate usage is sometimes referred to as
138    ///       "trust anchor assertion" and allows a domain name administrator to
139    ///       specify a new trust anchor -- for example, if the domain issues
140    ///       its own certificates under its own CA that is not expected to be
141    ///       in the end users' collection of trust anchors.  The target
142    ///       certificate MUST pass PKIX certification path validation, with any
143    ///       certificate matching the TLSA record considered to be a trust
144    ///       anchor for this certification path validation.
145    /// ```
146    #[cfg_attr(feature = "serde", serde(rename = "DANE-TA"))]
147    DaneTa,
148
149    /// ```text
150    ///       3 -- Certificate usage 3 is used to specify a certificate, or the
151    ///       public key of such a certificate, that MUST match the end entity
152    ///       certificate given by the server in TLS.  This certificate usage is
153    ///       sometimes referred to as "domain-issued certificate" because it
154    ///       allows for a domain name administrator to issue certificates for a
155    ///       domain without involving a third-party CA.  The target certificate
156    ///       MUST match the TLSA record.  The difference between certificate
157    ///       usage 1 and certificate usage 3 is that certificate usage 1
158    ///       requires that the certificate pass PKIX validation, but PKIX
159    ///       validation is not tested for certificate usage 3.
160    /// ```
161    #[cfg_attr(feature = "serde", serde(rename = "DANE-EE"))]
162    DaneEe,
163
164    /// Unassigned at the time of this implementation
165    Unassigned(u8),
166
167    /// Private usage
168    Private,
169}
170
171impl From<u8> for CertUsage {
172    fn from(usage: u8) -> Self {
173        match usage {
174            0 => Self::PkixTa,
175            1 => Self::PkixEe,
176            2 => Self::DaneTa,
177            3 => Self::DaneEe,
178            4..=254 => Self::Unassigned(usage),
179            255 => Self::Private,
180        }
181    }
182}
183
184impl From<CertUsage> for u8 {
185    fn from(usage: CertUsage) -> Self {
186        match usage {
187            CertUsage::PkixTa => 0,
188            CertUsage::PkixEe => 1,
189            CertUsage::DaneTa => 2,
190            CertUsage::DaneEe => 3,
191            CertUsage::Unassigned(usage) => usage,
192            CertUsage::Private => 255,
193        }
194    }
195}
196
197/// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2.1.1)
198///
199/// ```text
200/// 2.1.2.  The Selector Field
201///
202///    A one-octet value, called "selector", specifies which part of the TLS
203///    certificate presented by the server will be matched against the
204///    association data.  This value is defined in a new IANA registry (see
205///    Section 7.3).  The selectors defined in this document are:
206///
207///       0 -- Full
208///
209///       1 -- Spki
210///
211///    (Note that the use of "selector" in this document is completely
212///    unrelated to the use of "selector" in DomainKeys Identified Mail
213///    (DKIM) [RFC6376].)
214/// ```
215#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
216#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
217pub enum Selector {
218    /// Full certificate: the Certificate binary structure as defined in [RFC5280](https://tools.ietf.org/html/rfc5280)
219    #[cfg_attr(feature = "serde", serde(rename = "Cert"))]
220    Full,
221
222    /// SubjectPublicKeyInfo: DER-encoded binary structure as defined in [RFC5280](https://tools.ietf.org/html/rfc5280)
223    #[cfg_attr(feature = "serde", serde(rename = "SPKI"))]
224    Spki,
225
226    /// Unassigned at the time of this writing
227    Unassigned(u8),
228
229    /// Private usage
230    #[cfg_attr(feature = "serde", serde(rename = "PrivSel"))]
231    Private,
232}
233
234impl From<u8> for Selector {
235    fn from(selector: u8) -> Self {
236        match selector {
237            0 => Self::Full,
238            1 => Self::Spki,
239            2..=254 => Self::Unassigned(selector),
240            255 => Self::Private,
241        }
242    }
243}
244
245impl From<Selector> for u8 {
246    fn from(selector: Selector) -> Self {
247        match selector {
248            Selector::Full => 0,
249            Selector::Spki => 1,
250            Selector::Unassigned(selector) => selector,
251            Selector::Private => 255,
252        }
253    }
254}
255
256/// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2.1.3)
257///
258/// ```text
259/// 2.1.3.  The Matching Type Field
260///
261///    A one-octet value, called "matching type", specifies how the
262///    certificate association is presented.  This value is defined in a new
263///    IANA registry (see Section 7.4).  The types defined in this document
264///    are:
265///
266///       0 -- Raw
267///
268///       1 -- Sha256
269///
270///       2 -- Sha512
271///
272///    If the TLSA record's matching type is a hash, having the record use
273///    the same hash algorithm that was used in the signature in the
274///    certificate (if possible) will assist clients that support a small
275///    number of hash algorithms.
276/// ```
277#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
278#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
279pub enum Matching {
280    /// Exact match on selected content
281    #[cfg_attr(feature = "serde", serde(rename = "Full"))]
282    Raw,
283
284    /// SHA-256 hash of selected content [RFC6234](https://tools.ietf.org/html/rfc6234)
285    #[cfg_attr(feature = "serde", serde(rename = "SHA2-256"))]
286    Sha256,
287
288    /// SHA-512 hash of selected content [RFC6234](https://tools.ietf.org/html/rfc6234)
289    #[cfg_attr(feature = "serde", serde(rename = "SHA2-512"))]
290    Sha512,
291
292    /// Unassigned at the time of this writing
293    Unassigned(u8),
294
295    /// Private usage
296    #[cfg_attr(feature = "serde", serde(rename = "PrivMatch"))]
297    Private,
298}
299
300impl From<u8> for Matching {
301    fn from(matching: u8) -> Self {
302        match matching {
303            0 => Self::Raw,
304            1 => Self::Sha256,
305            2 => Self::Sha512,
306            3..=254 => Self::Unassigned(matching),
307            255 => Self::Private,
308        }
309    }
310}
311
312impl From<Matching> for u8 {
313    fn from(matching: Matching) -> Self {
314        match matching {
315            Matching::Raw => 0,
316            Matching::Sha256 => 1,
317            Matching::Sha512 => 2,
318            Matching::Unassigned(matching) => matching,
319            Matching::Private => 255,
320        }
321    }
322}
323
324impl TLSA {
325    /// Constructs a new TLSA
326    ///
327    /// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2)
328    ///
329    /// ```text
330    /// 2.  The TLSA Resource Record
331    ///
332    ///    The TLSA DNS resource record (RR) is used to associate a TLS server
333    ///    certificate or public key with the domain name where the record is
334    ///    found, thus forming a "TLSA certificate association".  The semantics
335    ///    of how the TLSA RR is interpreted are given later in this document.
336    ///
337    ///    The type value for the TLSA RR type is defined in Section 7.1.
338    ///
339    ///    The TLSA RR is class independent.
340    ///
341    ///    The TLSA RR has no special Time to Live (TTL) requirements.
342    /// ```
343    pub fn new(
344        cert_usage: CertUsage,
345        selector: Selector,
346        matching: Matching,
347        cert_data: Vec<u8>,
348    ) -> Self {
349        Self {
350            cert_usage,
351            selector,
352            matching,
353            cert_data,
354        }
355    }
356
357    /// Specifies the provided association that will be used to match the certificate presented in the TLS handshake
358    pub fn cert_usage(&self) -> CertUsage {
359        self.cert_usage
360    }
361
362    /// Specifies which part of the TLS certificate presented by the server will be matched against the association data
363    pub fn selector(&self) -> Selector {
364        self.selector
365    }
366
367    /// Specifies how the certificate association is presented
368    pub fn matching(&self) -> Matching {
369        self.matching
370    }
371
372    /// Binary data for validating the cert, see other members to understand format
373    pub fn cert_data(&self) -> &[u8] {
374        &self.cert_data
375    }
376}
377
378impl BinEncodable for TLSA {
379    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
380        encoder.emit_u8(self.cert_usage.into())?;
381        encoder.emit_u8(self.selector.into())?;
382        encoder.emit_u8(self.matching.into())?;
383        encoder.emit_vec(&self.cert_data)?;
384        Ok(())
385    }
386}
387
388impl RecordDataDecodable<'_> for TLSA {
389    /// Read the RData from the given Decoder
390    ///
391    /// ```text
392    ///                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
393    ///     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
394    ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395    ///    |  Cert. Usage  |   Selector    | Matching Type |               /
396    ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               /
397    ///    /                                                               /
398    ///    /                 Certificate Association Data                  /
399    ///    /                                                               /
400    ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401    /// ```
402    fn read_data(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<TLSA> {
403        let cert_usage = decoder.read_u8()?.unverified(/*CertUsage is verified*/).into();
404        let selector = decoder.read_u8()?.unverified(/*Selector is verified*/).into();
405        let matching = decoder.read_u8()?.unverified(/*Matching is verified*/).into();
406
407        // the remaining data is for the cert
408        let cert_len = rdata_length
409        .map(|u| u as usize)
410        .checked_sub(3)
411        .map_err(|_| ProtoError::from("invalid rdata length in TLSA"))?
412        .unverified(/*used purely as length safely*/);
413        let cert_data = decoder.read_vec(cert_len)?.unverified(/*will fail in usage if invalid*/);
414
415        Ok(Self {
416            cert_usage,
417            selector,
418            matching,
419            cert_data,
420        })
421    }
422}
423
424impl RecordData for TLSA {
425    fn try_from_rdata(data: RData) -> Result<Self, RData> {
426        match data {
427            RData::TLSA(data) => Ok(data),
428            _ => Err(data),
429        }
430    }
431
432    fn try_borrow(data: &RData) -> Option<&Self> {
433        match data {
434            RData::TLSA(data) => Some(data),
435            _ => None,
436        }
437    }
438
439    fn record_type(&self) -> RecordType {
440        RecordType::TLSA
441    }
442
443    fn into_rdata(self) -> RData {
444        RData::TLSA(self)
445    }
446}
447
448/// [RFC 6698, DNS-Based Authentication for TLS](https://tools.ietf.org/html/rfc6698#section-2.2)
449///
450/// ```text
451/// 2.2.  TLSA RR Presentation Format
452///
453///   The presentation format of the RDATA portion (as defined in
454///   [RFC1035]) is as follows:
455///
456///   o  The certificate usage field MUST be represented as an 8-bit
457///      unsigned integer.
458///
459///   o  The selector field MUST be represented as an 8-bit unsigned
460///      integer.
461///
462///   o  The matching type field MUST be represented as an 8-bit unsigned
463///      integer.
464///
465///   o  The certificate association data field MUST be represented as a
466///      string of hexadecimal characters.  Whitespace is allowed within
467///      the string of hexadecimal characters, as described in [RFC1035].
468///
469/// 2.3.  TLSA RR Examples
470///
471///    In the following examples, the domain name is formed using the rules
472///    in Section 3.
473///
474///    An example of a hashed (SHA-256) association of a PKIX CA
475///    certificate:
476///
477///    _443._tcp.www.example.com. IN TLSA (
478///       0 0 1 d2abde240d7cd3ee6b4b28c54df034b9
479///             7983a1d16e8a410e4561cb106618e971 )
480///
481///    An example of a hashed (SHA-512) subject public key association of a
482///    PKIX end entity certificate:
483///
484///    _443._tcp.www.example.com. IN TLSA (
485///       1 1 2 92003ba34942dc74152e2f2c408d29ec
486///             a5a520e7f2e06bb944f4dca346baf63c
487///             1b177615d466f6c4b71c216a50292bd5
488///             8c9ebdd2f74e38fe51ffd48c43326cbc )
489///
490///    An example of a full certificate association of a PKIX end entity
491///    certificate:
492///
493///    _443._tcp.www.example.com. IN TLSA (
494///       3 0 0 30820307308201efa003020102020... )
495/// ```
496impl fmt::Display for TLSA {
497    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
498        write!(
499            f,
500            "{usage} {selector} {matching} {cert}",
501            usage = u8::from(self.cert_usage),
502            selector = u8::from(self.selector),
503            matching = u8::from(self.matching),
504            cert = sshfp::HEX.encode(&self.cert_data),
505        )
506    }
507}
508
509#[cfg(test)]
510mod tests {
511    #![allow(clippy::dbg_macro, clippy::print_stdout)]
512
513    #[cfg(feature = "std")]
514    use std::println;
515
516    use super::*;
517
518    #[test]
519    fn read_cert_usage() {
520        assert_eq!(CertUsage::PkixTa, CertUsage::from(0));
521        assert_eq!(CertUsage::PkixEe, CertUsage::from(1));
522        assert_eq!(CertUsage::DaneTa, CertUsage::from(2));
523        assert_eq!(CertUsage::DaneEe, CertUsage::from(3));
524        assert_eq!(CertUsage::Unassigned(4), CertUsage::from(4));
525        assert_eq!(CertUsage::Unassigned(254), CertUsage::from(254));
526        assert_eq!(CertUsage::Private, CertUsage::from(255));
527
528        assert_eq!(u8::from(CertUsage::PkixTa), 0);
529        assert_eq!(u8::from(CertUsage::PkixEe), 1);
530        assert_eq!(u8::from(CertUsage::DaneTa), 2);
531        assert_eq!(u8::from(CertUsage::DaneEe), 3);
532        assert_eq!(u8::from(CertUsage::Unassigned(4)), 4);
533        assert_eq!(u8::from(CertUsage::Unassigned(254)), 254);
534        assert_eq!(u8::from(CertUsage::Private), 255);
535    }
536
537    #[test]
538    fn read_selector() {
539        assert_eq!(Selector::Full, Selector::from(0));
540        assert_eq!(Selector::Spki, Selector::from(1));
541        assert_eq!(Selector::Unassigned(2), Selector::from(2));
542        assert_eq!(Selector::Unassigned(254), Selector::from(254));
543        assert_eq!(Selector::Private, Selector::from(255));
544
545        assert_eq!(u8::from(Selector::Full), 0);
546        assert_eq!(u8::from(Selector::Spki), 1);
547        assert_eq!(u8::from(Selector::Unassigned(2)), 2);
548        assert_eq!(u8::from(Selector::Unassigned(254)), 254);
549        assert_eq!(u8::from(Selector::Private), 255);
550    }
551
552    #[test]
553    fn read_matching() {
554        assert_eq!(Matching::Raw, Matching::from(0));
555        assert_eq!(Matching::Sha256, Matching::from(1));
556        assert_eq!(Matching::Sha512, Matching::from(2));
557        assert_eq!(Matching::Unassigned(3), Matching::from(3));
558        assert_eq!(Matching::Unassigned(254), Matching::from(254));
559        assert_eq!(Matching::Private, Matching::from(255));
560
561        assert_eq!(u8::from(Matching::Raw), 0);
562        assert_eq!(u8::from(Matching::Sha256), 1);
563        assert_eq!(u8::from(Matching::Sha512), 2);
564        assert_eq!(u8::from(Matching::Unassigned(3)), 3);
565        assert_eq!(u8::from(Matching::Unassigned(254)), 254);
566        assert_eq!(u8::from(Matching::Private), 255);
567    }
568
569    fn test_encode_decode(rdata: TLSA) {
570        let mut bytes = Vec::new();
571        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
572        rdata.emit(&mut encoder).expect("failed to emit tlsa");
573        let bytes = encoder.into_bytes();
574
575        #[cfg(feature = "std")]
576        println!("bytes: {bytes:?}");
577
578        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
579        let read_rdata = TLSA::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
580            .expect("failed to read back");
581        assert_eq!(rdata, read_rdata);
582    }
583
584    #[test]
585    fn test_encode_decode_tlsa() {
586        test_encode_decode(TLSA::new(
587            CertUsage::PkixEe,
588            Selector::Spki,
589            Matching::Sha256,
590            vec![1, 2, 3, 4, 5, 6, 7, 8],
591        ));
592        test_encode_decode(TLSA::new(
593            CertUsage::PkixTa,
594            Selector::Full,
595            Matching::Raw,
596            vec![1, 2, 3, 4, 5, 6, 7, 8],
597        ));
598        test_encode_decode(TLSA::new(
599            CertUsage::DaneEe,
600            Selector::Full,
601            Matching::Sha512,
602            vec![1, 2, 3, 4, 5, 6, 7, 8],
603        ));
604        test_encode_decode(TLSA::new(
605            CertUsage::Unassigned(40),
606            Selector::Unassigned(39),
607            Matching::Unassigned(6),
608            vec![1, 2, 3, 4, 5, 6, 7, 8],
609        ));
610    }
611}