hickory_proto/dnssec/
mod.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//! dns security extension related modules
9
10use alloc::string::String;
11use alloc::vec::Vec;
12use core::fmt;
13
14#[cfg(feature = "backtrace")]
15use backtrace::Backtrace;
16use rdata::tsig::TsigAlgorithm;
17#[cfg(feature = "serde")]
18use serde::{Deserialize, Serialize};
19use thiserror::Error;
20
21use crate::error::{ProtoError, ProtoErrorKind};
22#[cfg(feature = "backtrace")]
23use crate::trace;
24
25mod algorithm;
26mod dnssec_dns_handle;
27pub use dnssec_dns_handle::DnssecDnsHandle;
28#[doc(hidden)]
29pub use dnssec_dns_handle::verify_nsec;
30/// Cryptographic backend implementations of DNSSEC traits.
31pub mod crypto;
32mod ec_public_key;
33mod nsec3;
34pub mod proof;
35pub mod public_key;
36pub mod rdata;
37mod rsa_public_key;
38mod signer;
39mod supported_algorithm;
40pub mod tbs;
41mod trust_anchor;
42pub mod tsig;
43mod verifier;
44
45pub use self::algorithm::Algorithm;
46pub use self::nsec3::Nsec3HashAlgorithm;
47pub use self::proof::{Proof, ProofError, ProofErrorKind, ProofFlags, Proven};
48pub use self::public_key::{PublicKey, PublicKeyBuf};
49pub use self::signer::SigSigner;
50pub use self::supported_algorithm::SupportedAlgorithms;
51pub use self::tbs::TBS;
52pub use self::trust_anchor::TrustAnchors;
53pub use self::verifier::Verifier;
54
55/// DNSSEC Delegation Signer (DS) Resource Record (RR) Type Digest Algorithms
56///
57/// [IANA Registry](https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml)
58/// ```text
59/// Value    Description           Status       Reference
60///  0        Reserved              -            [RFC3658]
61///  1        SHA-1                 MANDATORY    [RFC3658]
62///  2        SHA-256               MANDATORY    [RFC4509]
63///  3        GOST R 34.11-94       DEPRECATED   [RFC5933][Change the status of GOST Signature Algorithms in DNSSEC in the IETF stream to Historic]
64///  4        SHA-384               OPTIONAL     [RFC6605]
65///  5        GOST R 34.11-2012     OPTIONAL     [RFC9558]
66///  6        SM3                   OPTIONAL     [RFC9563]
67/// ```
68///
69/// <https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml>
70#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
71#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
72#[non_exhaustive]
73pub enum DigestType {
74    /// [RFC 3658](https://tools.ietf.org/html/rfc3658)
75    #[cfg_attr(feature = "serde", serde(rename = "SHA-1"))]
76    SHA1,
77    /// [RFC 4509](https://tools.ietf.org/html/rfc4509)
78    #[cfg_attr(feature = "serde", serde(rename = "SHA-256"))]
79    SHA256,
80    /// [RFC 6605](https://tools.ietf.org/html/rfc6605)
81    #[cfg_attr(feature = "serde", serde(rename = "SHA-384"))]
82    SHA384,
83    /// An unknown digest type
84    Unknown(u8),
85}
86
87impl DigestType {
88    fn is_supported(&self) -> bool {
89        !matches!(self, Self::Unknown(_))
90    }
91}
92
93impl From<u8> for DigestType {
94    fn from(value: u8) -> Self {
95        match value {
96            1 => Self::SHA1,
97            2 => Self::SHA256,
98            4 => Self::SHA384,
99            _ => Self::Unknown(value),
100        }
101    }
102}
103
104impl From<DigestType> for u8 {
105    fn from(a: DigestType) -> Self {
106        match a {
107            DigestType::SHA1 => 1,
108            DigestType::SHA256 => 2,
109            DigestType::SHA384 => 4,
110            DigestType::Unknown(other) => other,
111        }
112    }
113}
114
115/// A key that can be used to sign records.
116pub trait SigningKey: Send + Sync + 'static {
117    /// Sign DNS records.
118    ///
119    /// # Return value
120    ///
121    /// The signature, ready to be stored in an `RData::RRSIG`.
122    fn sign(&self, tbs: &TBS) -> DnsSecResult<Vec<u8>>;
123
124    /// Returns a [`PublicKeyBuf`] for this [`SigningKey`].
125    fn to_public_key(&self) -> DnsSecResult<PublicKeyBuf>;
126
127    /// Returns the algorithm of the key.
128    fn algorithm(&self) -> Algorithm;
129}
130
131/// The format of the binary key
132#[derive(Clone, Copy, Debug, Eq, PartialEq)]
133pub enum KeyFormat {
134    /// A der encoded key
135    Der,
136    /// A pem encoded key, the default of OpenSSL
137    Pem,
138    /// Pkcs8, a pkcs8 formatted private key
139    Pkcs8,
140}
141
142/// An alias for dnssec results returned by functions of this crate
143pub type DnsSecResult<T> = ::core::result::Result<T, DnsSecError>;
144
145/// The error type for dnssec errors that get returned in the crate
146#[derive(Debug, Clone, Error)]
147pub struct DnsSecError {
148    kind: DnsSecErrorKind,
149    #[cfg(feature = "backtrace")]
150    backtrack: Option<Backtrace>,
151}
152
153impl DnsSecError {
154    /// Get the kind of the error
155    pub fn kind(&self) -> &DnsSecErrorKind {
156        &self.kind
157    }
158}
159
160impl fmt::Display for DnsSecError {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        cfg_if::cfg_if! {
163            if #[cfg(feature = "backtrace")] {
164                if let Some(backtrace) = &self.backtrack {
165                    fmt::Display::fmt(&self.kind, f)?;
166                    fmt::Debug::fmt(backtrace, f)
167                } else {
168                    fmt::Display::fmt(&self.kind, f)
169                }
170            } else {
171                fmt::Display::fmt(&self.kind, f)
172            }
173        }
174    }
175}
176
177impl From<DnsSecErrorKind> for DnsSecError {
178    fn from(kind: DnsSecErrorKind) -> Self {
179        Self {
180            kind,
181            #[cfg(feature = "backtrace")]
182            backtrack: trace!(),
183        }
184    }
185}
186
187impl From<&'static str> for DnsSecError {
188    fn from(msg: &'static str) -> Self {
189        DnsSecErrorKind::Message(msg).into()
190    }
191}
192
193impl From<String> for DnsSecError {
194    fn from(msg: String) -> Self {
195        DnsSecErrorKind::Msg(msg).into()
196    }
197}
198
199impl From<ProtoError> for DnsSecError {
200    fn from(e: ProtoError) -> Self {
201        match e.kind() {
202            ProtoErrorKind::Timeout => DnsSecErrorKind::Timeout.into(),
203            _ => DnsSecErrorKind::from(e).into(),
204        }
205    }
206}
207
208impl From<ring_like::KeyRejected> for DnsSecError {
209    fn from(e: ring_like::KeyRejected) -> Self {
210        DnsSecErrorKind::from(e).into()
211    }
212}
213
214impl From<ring_like::Unspecified> for DnsSecError {
215    fn from(e: ring_like::Unspecified) -> Self {
216        DnsSecErrorKind::from(e).into()
217    }
218}
219
220/// The error kind for dnssec errors that get returned in the crate
221#[derive(Debug, Error)]
222#[non_exhaustive]
223pub enum DnsSecErrorKind {
224    /// An HMAC failed to verify
225    #[error("hmac validation failure")]
226    HmacInvalid,
227
228    /// An error with an arbitrary message, referenced as &'static str
229    #[error("{0}")]
230    Message(&'static str),
231
232    /// An error with an arbitrary message, stored as String
233    #[error("{0}")]
234    Msg(String),
235
236    // foreign
237    /// An error got returned by the hickory-proto crate
238    #[error("proto error: {0}")]
239    Proto(#[from] ProtoError),
240
241    /// A ring error
242    #[error("ring error: {0}")]
243    RingKeyRejected(#[from] ring_like::KeyRejected),
244
245    /// A ring error
246    #[error("ring error: {0}")]
247    RingUnspecified(#[from] ring_like::Unspecified),
248
249    /// A request timed out
250    #[error("request timed out")]
251    Timeout,
252
253    /// Tsig unsupported mac algorithm
254    /// Supported algorithm documented in `TsigAlgorithm::supported` function.
255    #[error("Tsig unsupported mac algorithm")]
256    TsigUnsupportedMacAlgorithm(TsigAlgorithm),
257
258    /// Tsig key verification failed
259    #[error("Tsig key wrong key error")]
260    TsigWrongKey,
261}
262
263impl Clone for DnsSecErrorKind {
264    fn clone(&self) -> Self {
265        use DnsSecErrorKind::*;
266        match self {
267            HmacInvalid => HmacInvalid,
268            Message(msg) => Message(msg),
269            Msg(msg) => Msg(msg.clone()),
270            // foreign
271            Proto(proto) => Proto(proto.clone()),
272            RingKeyRejected(r) => Msg(format!("Ring rejected key: {r}")),
273            RingUnspecified(_r) => RingUnspecified(ring_like::Unspecified),
274            Timeout => Timeout,
275            TsigUnsupportedMacAlgorithm(ref alg) => TsigUnsupportedMacAlgorithm(alg.clone()),
276            TsigWrongKey => TsigWrongKey,
277        }
278    }
279}
280
281#[cfg(all(feature = "dnssec-aws-lc-rs", not(feature = "dnssec-ring")))]
282pub(crate) use aws_lc_rs_impl as ring_like;
283#[cfg(feature = "dnssec-ring")]
284pub(crate) use ring_impl as ring_like;
285
286#[cfg(feature = "dnssec-aws-lc-rs")]
287#[cfg_attr(feature = "dnssec-ring", allow(unused_imports))]
288pub(crate) mod aws_lc_rs_impl {
289    pub(crate) use aws_lc_rs::{
290        digest,
291        error::{KeyRejected, Unspecified},
292        hmac,
293        rand::SystemRandom,
294        rsa::PublicKeyComponents,
295        signature::{
296            self, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
297            ED25519_PUBLIC_KEY_LEN, EcdsaKeyPair, Ed25519KeyPair, KeyPair, RSA_PKCS1_SHA256,
298            RSA_PKCS1_SHA512, RsaKeyPair,
299        },
300    };
301}
302
303#[cfg(feature = "dnssec-ring")]
304pub(crate) mod ring_impl {
305    pub(crate) use ring::{
306        digest,
307        error::{KeyRejected, Unspecified},
308        hmac,
309        rand::SystemRandom,
310        rsa::PublicKeyComponents,
311        signature::{
312            self, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
313            ED25519_PUBLIC_KEY_LEN, EcdsaKeyPair, Ed25519KeyPair, KeyPair, RSA_PKCS1_SHA256,
314            RSA_PKCS1_SHA512, RsaKeyPair,
315        },
316    };
317}
318
319#[cfg(test)]
320mod test_utils {
321    use rdata::DNSKEY;
322
323    use super::*;
324
325    pub(super) fn public_key_test(key: &dyn SigningKey) {
326        let pk = key.to_public_key().unwrap();
327
328        let tbs = TBS::from(&b"www.example.com"[..]);
329        let mut sig = key.sign(&tbs).unwrap();
330        assert!(
331            pk.verify(tbs.as_ref(), &sig).is_ok(),
332            "public_key_test() failed to verify (algorithm: {:?})",
333            key.algorithm(),
334        );
335        sig[10] = !sig[10];
336        assert!(
337            pk.verify(tbs.as_ref(), &sig).is_err(),
338            "algorithm: {:?} (public key, neg)",
339            key.algorithm(),
340        );
341    }
342
343    pub(super) fn hash_test(key: &dyn SigningKey, neg: &dyn SigningKey) {
344        let tbs = TBS::from(&b"www.example.com"[..]);
345
346        // TODO: convert to stored keys...
347        let pub_key = key.to_public_key().unwrap();
348        let neg_pub_key = neg.to_public_key().unwrap();
349
350        let sig = key.sign(&tbs).unwrap();
351        assert!(
352            pub_key.verify(tbs.as_ref(), &sig).is_ok(),
353            "algorithm: {:?}",
354            key.algorithm(),
355        );
356
357        let pub_key = key.to_public_key().unwrap();
358        let dns_key = DNSKEY::from_key(&pub_key);
359        assert!(
360            dns_key.verify(tbs.as_ref(), &sig).is_ok(),
361            "algorithm: {:?} (dnskey)",
362            pub_key.algorithm(),
363        );
364        assert!(
365            neg_pub_key.verify(tbs.as_ref(), &sig).is_err(),
366            "algorithm: {:?} (neg)",
367            neg_pub_key.algorithm(),
368        );
369
370        let neg_pub_key = neg.to_public_key().unwrap();
371        let neg_dns_key = DNSKEY::from_key(&neg_pub_key);
372        assert!(
373            neg_dns_key.verify(tbs.as_ref(), &sig).is_err(),
374            "algorithm: {:?} (dnskey, neg)",
375            neg_pub_key.algorithm(),
376        );
377    }
378}