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