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}