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}