hickory_proto/rr/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 std::fmt;
13use std::fmt::{Display, Formatter};
14
15#[cfg(feature = "serde-config")]
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-config", 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    RSASHA1NSEC3SHA1,
126    /// RSA public key with SHA256 hash
127    RSASHA256,
128    /// RSA public key with SHA512 hash
129    RSASHA512,
130    /// [rfc6605](https://tools.ietf.org/html/rfc6605)
131    ECDSAP256SHA256,
132    /// [rfc6605](https://tools.ietf.org/html/rfc6605)
133    ECDSAP384SHA384,
134    /// [draft-ietf-curdle-dnskey-eddsa-03](https://tools.ietf.org/html/draft-ietf-curdle-dnskey-eddsa-03)
135    ED25519,
136    /// An unknown algorithm identifier
137    Unknown(u8),
138}
139
140impl Algorithm {
141    /// <https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml>
142    pub fn from_u8(value: u8) -> Self {
143        #[allow(deprecated)]
144        match value {
145            1 => Self::RSAMD5,
146            3 => Self::DSA,
147            5 => Self::RSASHA1,
148            7 => Self::RSASHA1NSEC3SHA1,
149            8 => Self::RSASHA256,
150            10 => Self::RSASHA512,
151            13 => Self::ECDSAP256SHA256,
152            14 => Self::ECDSAP384SHA384,
153            15 => Self::ED25519,
154            _ => Self::Unknown(value),
155        }
156    }
157
158    /// length in bytes that the hash portion of this function will produce
159    pub fn hash_len(self) -> Option<usize> {
160        match self {
161            Self::RSAMD5 => Some(16),                                       // 128 bits
162            Self::DSA | Self::RSASHA1 | Self::RSASHA1NSEC3SHA1 => Some(20), // 160 bits
163            Self::RSASHA256 | Self::ECDSAP256SHA256 | Self::ED25519 => Some(32), // 256 bits
164            Self::ECDSAP384SHA384 => Some(48),
165            Self::RSASHA512 => Some(64), // 512 bites
166            Self::Unknown(_) => None,
167        }
168    }
169
170    /// Convert to string form
171    #[deprecated(note = "use as_str instead")]
172    pub fn to_str(self) -> &'static str {
173        self.as_str()
174    }
175
176    /// Convert to string form
177    pub fn as_str(self) -> &'static str {
178        match self {
179            Self::RSAMD5 => "RSAMD5",
180            Self::DSA => "DSA",
181            Self::RSASHA1 => "RSASHA1",
182            Self::RSASHA256 => "RSASHA256",
183            Self::RSASHA1NSEC3SHA1 => "RSASHA1-NSEC3-SHA1",
184            Self::RSASHA512 => "RSASHA512",
185            Self::ECDSAP256SHA256 => "ECDSAP256SHA256",
186            Self::ECDSAP384SHA384 => "ECDSAP384SHA384",
187            Self::ED25519 => "ED25519",
188            Self::Unknown(_) => "Unknown",
189        }
190    }
191}
192
193impl BinEncodable for Algorithm {
194    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
195        encoder.emit(u8::from(*self))
196    }
197}
198
199impl<'r> BinDecodable<'r> for Algorithm {
200    // https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
201    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
202        let algorithm_id =
203            decoder.read_u8()?.unverified(/*Algorithm is verified as safe in processing this*/);
204        Ok(Self::from_u8(algorithm_id))
205    }
206}
207
208impl From<Algorithm> for &'static str {
209    fn from(a: Algorithm) -> &'static str {
210        a.as_str()
211    }
212}
213
214impl From<Algorithm> for u8 {
215    fn from(a: Algorithm) -> Self {
216        match a {
217            Algorithm::RSAMD5 => 1,
218            Algorithm::DSA => 3,
219            Algorithm::RSASHA1 => 5,
220            Algorithm::RSASHA1NSEC3SHA1 => 7,
221            Algorithm::RSASHA256 => 8,
222            Algorithm::RSASHA512 => 10,
223            Algorithm::ECDSAP256SHA256 => 13,
224            Algorithm::ECDSAP384SHA384 => 14,
225            Algorithm::ED25519 => 15,
226            Algorithm::Unknown(v) => v,
227        }
228    }
229}
230
231impl Display for Algorithm {
232    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
233        f.write_str(self.as_str())
234    }
235}
236
237#[test]
238fn test_into() {
239    for algorithm in &[
240        Algorithm::RSAMD5,
241        Algorithm::DSA,
242        Algorithm::RSASHA1,
243        Algorithm::RSASHA256,
244        Algorithm::RSASHA1NSEC3SHA1,
245        Algorithm::RSASHA512,
246        Algorithm::ECDSAP256SHA256,
247        Algorithm::ECDSAP384SHA384,
248        Algorithm::ED25519,
249    ] {
250        assert_eq!(*algorithm, Algorithm::from_u8(Into::<u8>::into(*algorithm)))
251    }
252}
253
254#[test]
255fn test_order() {
256    let mut algorithms = [
257        Algorithm::RSAMD5,
258        Algorithm::DSA,
259        Algorithm::RSASHA1,
260        Algorithm::RSASHA256,
261        Algorithm::RSASHA1NSEC3SHA1,
262        Algorithm::RSASHA512,
263        Algorithm::ECDSAP256SHA256,
264        Algorithm::ECDSAP384SHA384,
265        Algorithm::ED25519,
266    ];
267
268    algorithms.sort();
269
270    for (got, expect) in algorithms.iter().zip(
271        [
272            Algorithm::RSAMD5,
273            Algorithm::DSA,
274            Algorithm::RSASHA1,
275            Algorithm::RSASHA1NSEC3SHA1,
276            Algorithm::RSASHA256,
277            Algorithm::RSASHA512,
278            Algorithm::ECDSAP256SHA256,
279            Algorithm::ECDSAP384SHA384,
280            Algorithm::ED25519,
281        ]
282        .iter(),
283    ) {
284        assert_eq!(got, expect);
285    }
286}