trust_dns_proto/rr/rdata/
tlsa.rs

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