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}