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}