hickory_proto/dnssec/
algorithm.rs

1// Copyright 2015-2022 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// needed for the derive statements on algorithm
9//   this issue in rustc would help narrow the statement: https://github.com/rust-lang/rust/issues/62398
10#![allow(deprecated, clippy::use_self)]
11
12use alloc::fmt;
13use core::fmt::{Display, Formatter};
14
15#[cfg(feature = "serde")]
16use serde::{Deserialize, Serialize};
17
18use crate::error::*;
19use crate::serialize::binary::*;
20
21/// DNSSEC signing and validation algorithms.
22///
23/// For [reference](https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml)
24///  the iana documents have all the officially registered algorithms.
25///
26/// [RFC 6944](https://tools.ietf.org/html/rfc6944), DNSSEC DNSKEY Algorithm Status, April 2013
27///
28/// ```text
29///
30/// 2.2.  Algorithm Implementation Status Assignment Rationale
31///
32/// RSASHA1 has an implementation status of Must Implement, consistent
33/// with [RFC4034].  RSAMD5 has an implementation status of Must Not
34/// Implement because of known weaknesses in MD5.
35///
36/// The status of RSASHA1-NSEC3-SHA1 is set to Recommended to Implement
37/// as many deployments use NSEC3.  The status of RSA/SHA-256 and RSA/
38/// SHA-512 are also set to Recommended to Implement as major deployments
39/// (such as the root zone) use these algorithms [ROOTDPS].  It is
40/// believed that RSA/SHA-256 or RSA/SHA-512 algorithms will replace
41/// older algorithms (e.g., RSA/SHA-1) that have a perceived weakness.
42///
43/// Likewise, ECDSA with the two identified curves (ECDSAP256SHA256 and
44/// ECDSAP384SHA384) is an algorithm that may see widespread use due to
45/// the perceived similar level of security offered with smaller key size
46/// compared to the key sizes of algorithms such as RSA.  Therefore,
47/// ECDSAP256SHA256 and ECDSAP384SHA384 are Recommended to Implement.
48///
49/// All other algorithms used in DNSSEC specified without an
50/// implementation status are currently set to Optional.
51///
52/// 2.3.  DNSSEC Implementation Status Table
53///
54/// The DNSSEC algorithm implementation status table is listed below.
55/// Only the algorithms already specified for use with DNSSEC at the time
56/// of writing are listed.
57///
58///  +------------+------------+-------------------+-------------------+
59///  |    Must    |  Must Not  |    Recommended    |      Optional     |
60///  |  Implement | Implement  |   to Implement    |                   |
61///  +------------+------------+-------------------+-------------------+
62///  |            |            |                   |                   |
63///  |   RSASHA1  |   RSAMD5   |   RSASHA256       |   Any             |
64///  |            |            |   RSASHA1-NSEC3   |   registered      |
65///  |            |            |    -SHA1          |   algorithm       |
66///  |            |            |   RSASHA512       |   not listed in   |
67///  |            |            |   ECDSAP256SHA256 |   this table      |
68///  |            |            |   ECDSAP384SHA384 |                   |
69///  +------------+------------+-------------------+-------------------+
70///
71///    This table does not list the Reserved values in the IANA registry
72///    table or the values for INDIRECT (252), PRIVATE (253), and PRIVATEOID
73///    (254).  These values may relate to more than one algorithm and are
74///    therefore up to the implementer's discretion.  As noted, any
75///    algorithm not listed in the table is Optional.  As of this writing,
76///    the Optional algorithms are DSASHA1, DH, DSA-NSEC3-SHA1, and GOST-
77///    ECC, but in general, anything not explicitly listed is Optional.
78///
79/// 2.4.  Specifying New Algorithms and Updating the Status of Existing
80///       Entries
81///
82///    [RFC6014] establishes a parallel procedure for adding a registry
83///    entry for a new algorithm other than a standards track document.
84///    Because any algorithm not listed in the foregoing table is Optional,
85///    algorithms entered into the registry using the [RFC6014] procedure
86///    are automatically Optional.
87///
88///    It has turned out to be useful for implementations to refer to a
89///    single document that specifies the implementation status of every
90///    algorithm.  Accordingly, when a new algorithm is to be registered
91///    with a status other than Optional, this document shall be made
92///    obsolete by a new document that adds the new algorithm to the table
93///    in Section 2.3.  Similarly, if the status of any algorithm in the
94///    table in Section 2.3 changes, a new document shall make this document
95///    obsolete; that document shall include a replacement of the table in
96///    Section 2.3.  This way, the goal of having one authoritative document
97///    to specify all the status values is achieved.
98///
99///    This document cannot be updated, only made obsolete and replaced by a
100///    successor document.
101/// ```
102#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
103#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
104#[non_exhaustive]
105pub enum Algorithm {
106    /// DO NOT USE, MD5 is a compromised hashing function, it is here for backward compatibility
107    #[deprecated(
108        note = "this is a compromised hashing function, it is here for backward compatibility"
109    )]
110    RSAMD5,
111    /// DO NOT USE, DSA is a compromised hashing function, it is here for backward compatibility
112    #[deprecated(
113        note = "this is a compromised hashing function, it is here for backward compatibility"
114    )]
115    DSA,
116    /// DO NOT USE, SHA1 is a compromised hashing function, it is here for backward compatibility
117    #[deprecated(
118        note = "this is a compromised hashing function, it is here for backward compatibility"
119    )]
120    RSASHA1,
121    /// DO NOT USE, SHA1 is a compromised hashing function, it is here for backward compatibility
122    #[deprecated(
123        note = "this is a compromised hashing function, it is here for backward compatibility"
124    )]
125    #[cfg_attr(feature = "serde", serde(rename = "RSASHA1-NSEC3-SHA1"))]
126    RSASHA1NSEC3SHA1,
127    /// RSA public key with SHA256 hash
128    RSASHA256,
129    /// RSA public key with SHA512 hash
130    RSASHA512,
131    /// [rfc6605](https://tools.ietf.org/html/rfc6605)
132    ECDSAP256SHA256,
133    /// [rfc6605](https://tools.ietf.org/html/rfc6605)
134    ECDSAP384SHA384,
135    /// [draft-ietf-curdle-dnskey-eddsa-03](https://tools.ietf.org/html/draft-ietf-curdle-dnskey-eddsa-03)
136    ED25519,
137    /// An unknown algorithm identifier
138    Unknown(u8),
139}
140
141impl Algorithm {
142    /// <https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml>
143    pub fn from_u8(value: u8) -> Self {
144        #[allow(deprecated)]
145        match value {
146            1 => Self::RSAMD5,
147            3 => Self::DSA,
148            5 => Self::RSASHA1,
149            7 => Self::RSASHA1NSEC3SHA1,
150            8 => Self::RSASHA256,
151            10 => Self::RSASHA512,
152            13 => Self::ECDSAP256SHA256,
153            14 => Self::ECDSAP384SHA384,
154            15 => Self::ED25519,
155            _ => Self::Unknown(value),
156        }
157    }
158
159    /// Whether this algorithm is supported by hickory's build settings
160    pub fn is_supported(&self) -> bool {
161        matches!(
162            self,
163            Algorithm::ECDSAP256SHA256
164                | Algorithm::ECDSAP384SHA384
165                | Algorithm::ED25519
166                | Algorithm::RSASHA1
167                | Algorithm::RSASHA1NSEC3SHA1
168                | Algorithm::RSASHA256
169                | Algorithm::RSASHA512
170        )
171    }
172
173    /// length in bytes that the hash portion of this function will produce
174    pub fn hash_len(self) -> Option<usize> {
175        match self {
176            Self::RSAMD5 => Some(16),                                       // 128 bits
177            Self::DSA | Self::RSASHA1 | Self::RSASHA1NSEC3SHA1 => Some(20), // 160 bits
178            Self::RSASHA256 | Self::ECDSAP256SHA256 | Self::ED25519 => Some(32), // 256 bits
179            Self::ECDSAP384SHA384 => Some(48),
180            Self::RSASHA512 => Some(64), // 512 bites
181            Self::Unknown(_) => None,
182        }
183    }
184
185    /// Convert to string form
186    #[deprecated(note = "use as_str instead")]
187    pub fn to_str(self) -> &'static str {
188        self.as_str()
189    }
190
191    /// Convert to string form
192    pub fn as_str(self) -> &'static str {
193        match self {
194            Self::RSAMD5 => "RSAMD5",
195            Self::DSA => "DSA",
196            Self::RSASHA1 => "RSASHA1",
197            Self::RSASHA256 => "RSASHA256",
198            Self::RSASHA1NSEC3SHA1 => "RSASHA1-NSEC3-SHA1",
199            Self::RSASHA512 => "RSASHA512",
200            Self::ECDSAP256SHA256 => "ECDSAP256SHA256",
201            Self::ECDSAP384SHA384 => "ECDSAP384SHA384",
202            Self::ED25519 => "ED25519",
203            Self::Unknown(_) => "Unknown",
204        }
205    }
206}
207
208impl BinEncodable for Algorithm {
209    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
210        encoder.emit(u8::from(*self))
211    }
212}
213
214impl<'r> BinDecodable<'r> for Algorithm {
215    // https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
216    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
217        let algorithm_id =
218            decoder.read_u8()?.unverified(/*Algorithm is verified as safe in processing this*/);
219        Ok(Self::from_u8(algorithm_id))
220    }
221}
222
223impl From<Algorithm> for &'static str {
224    fn from(a: Algorithm) -> &'static str {
225        a.as_str()
226    }
227}
228
229impl From<Algorithm> for u8 {
230    fn from(a: Algorithm) -> Self {
231        match a {
232            Algorithm::RSAMD5 => 1,
233            Algorithm::DSA => 3,
234            Algorithm::RSASHA1 => 5,
235            Algorithm::RSASHA1NSEC3SHA1 => 7,
236            Algorithm::RSASHA256 => 8,
237            Algorithm::RSASHA512 => 10,
238            Algorithm::ECDSAP256SHA256 => 13,
239            Algorithm::ECDSAP384SHA384 => 14,
240            Algorithm::ED25519 => 15,
241            Algorithm::Unknown(v) => v,
242        }
243    }
244}
245
246impl Display for Algorithm {
247    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
248        f.write_str(self.as_str())
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[test]
257    fn test_into() {
258        for algorithm in &[
259            Algorithm::RSAMD5,
260            Algorithm::DSA,
261            Algorithm::RSASHA1,
262            Algorithm::RSASHA256,
263            Algorithm::RSASHA1NSEC3SHA1,
264            Algorithm::RSASHA512,
265            Algorithm::ECDSAP256SHA256,
266            Algorithm::ECDSAP384SHA384,
267            Algorithm::ED25519,
268        ] {
269            assert_eq!(*algorithm, Algorithm::from_u8(Into::<u8>::into(*algorithm)))
270        }
271    }
272
273    #[test]
274    fn test_order() {
275        let mut algorithms = [
276            Algorithm::RSAMD5,
277            Algorithm::DSA,
278            Algorithm::RSASHA1,
279            Algorithm::RSASHA256,
280            Algorithm::RSASHA1NSEC3SHA1,
281            Algorithm::RSASHA512,
282            Algorithm::ECDSAP256SHA256,
283            Algorithm::ECDSAP384SHA384,
284            Algorithm::ED25519,
285        ];
286
287        algorithms.sort();
288
289        for (got, expect) in algorithms.iter().zip(
290            [
291                Algorithm::RSAMD5,
292                Algorithm::DSA,
293                Algorithm::RSASHA1,
294                Algorithm::RSASHA1NSEC3SHA1,
295                Algorithm::RSASHA256,
296                Algorithm::RSASHA512,
297                Algorithm::ECDSAP256SHA256,
298                Algorithm::ECDSAP384SHA384,
299                Algorithm::ED25519,
300            ]
301            .iter(),
302        ) {
303            assert_eq!(got, expect);
304        }
305    }
306}