1#[cfg(feature = "openssl")]
2use openssl::ec::EcKey;
3#[cfg(feature = "openssl")]
4use openssl::rsa::Rsa;
5#[cfg(feature = "openssl")]
6use openssl::symm::Cipher;
7#[cfg(feature = "ring")]
8use ring::signature::{
9 EcdsaKeyPair, Ed25519KeyPair, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
10};
11
12use crate::error::*;
13use crate::rr::dnssec::{Algorithm, KeyPair, Private};
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub enum KeyFormat {
18 Der,
20 Pem,
22 Pkcs8,
24}
25
26impl KeyFormat {
27 #[allow(unused, clippy::match_single_binding)]
29 pub fn decode_key(
30 self,
31 bytes: &[u8],
32 password: Option<&str>,
33 algorithm: Algorithm,
34 ) -> DnsSecResult<KeyPair<Private>> {
35 let password = password.unwrap_or("");
37 let password = password.as_bytes();
38
39 #[allow(deprecated)]
40 match algorithm {
41 Algorithm::Unknown(v) => Err(format!("unknown algorithm: {v}").into()),
42 #[cfg(feature = "openssl")]
43 e @ Algorithm::RSASHA1 | e @ Algorithm::RSASHA1NSEC3SHA1 => {
44 Err(format!("unsupported Algorithm (insecure): {e:?}").into())
45 }
46 #[cfg(feature = "openssl")]
47 Algorithm::RSASHA256 | Algorithm::RSASHA512 => {
48 let key = match self {
49 Self::Der => Rsa::private_key_from_der(bytes)
50 .map_err(|e| format!("error reading RSA as DER: {e}"))?,
51 Self::Pem => {
52 let key = Rsa::private_key_from_pem_passphrase(bytes, password);
53
54 key.map_err(|e| {
55 format!("could not decode RSA from PEM, bad password?: {e}")
56 })?
57 }
58 e => {
59 return Err(format!(
60 "unsupported key format with RSA (DER or PEM only): \
61 {e:?}"
62 )
63 .into())
64 }
65 };
66
67 Ok(KeyPair::from_rsa(key)
68 .map_err(|e| format!("could not translate RSA to KeyPair: {e}"))?)
69 }
70 Algorithm::ECDSAP256SHA256 | Algorithm::ECDSAP384SHA384 => match self {
71 #[cfg(feature = "openssl")]
72 Self::Der => {
73 let key = EcKey::private_key_from_der(bytes)
74 .map_err(|e| format!("error reading EC as DER: {e}"))?;
75
76 Ok(KeyPair::from_ec_key(key)
77 .map_err(|e| format!("could not translate RSA to KeyPair: {e}"))?)
78 }
79 #[cfg(feature = "openssl")]
80 Self::Pem => {
81 let key = EcKey::private_key_from_pem_passphrase(bytes, password)
82 .map_err(|e| format!("could not decode EC from PEM, bad password?: {e}"))?;
83
84 Ok(KeyPair::from_ec_key(key)
85 .map_err(|e| format!("could not translate RSA to KeyPair: {e}"))?)
86 }
87 #[cfg(feature = "ring")]
88 Self::Pkcs8 => {
89 let ring_algorithm = if algorithm == Algorithm::ECDSAP256SHA256 {
90 &ECDSA_P256_SHA256_FIXED_SIGNING
91 } else {
92 &ECDSA_P384_SHA384_FIXED_SIGNING
93 };
94 let rng = ring::rand::SystemRandom::new();
95 let key = EcdsaKeyPair::from_pkcs8(ring_algorithm, bytes, &rng)?;
96
97 Ok(KeyPair::from_ecdsa(key))
98 }
99 e => Err(format!("unsupported key format with EC: {e:?}").into()),
100 },
101 Algorithm::ED25519 => match self {
102 #[cfg(feature = "ring")]
103 Self::Pkcs8 => {
104 let key = Ed25519KeyPair::from_pkcs8(bytes)?;
105
106 Ok(KeyPair::from_ed25519(key))
107 }
108 e => Err(format!(
109 "unsupported key format with ED25519 (only Pkcs8 supported): {e:?}"
110 )
111 .into()),
112 },
113 e => {
114 Err(format!("unsupported Algorithm, enable openssl or ring feature: {e:?}").into())
115 }
116 }
117 }
118
119 pub fn generate_and_encode(
121 self,
122 algorithm: Algorithm,
123 password: Option<&str>,
124 ) -> DnsSecResult<Vec<u8>> {
125 #[allow(unused)]
127 let password = password
128 .iter()
129 .filter(|s| !s.is_empty())
130 .map(|s| s.as_bytes())
131 .next();
132
133 #[allow(unused, deprecated)]
135 let key_pair: KeyPair<Private> = match algorithm {
136 Algorithm::Unknown(v) => return Err(format!("unknown algorithm: {v}").into()),
137 #[cfg(feature = "openssl")]
138 e @ Algorithm::RSASHA1 | e @ Algorithm::RSASHA1NSEC3SHA1 => {
139 return Err(format!("unsupported Algorithm (insecure): {e:?}").into())
140 }
141 #[cfg(feature = "openssl")]
142 Algorithm::RSASHA256 | Algorithm::RSASHA512 => KeyPair::generate(algorithm)?,
143 Algorithm::ECDSAP256SHA256 | Algorithm::ECDSAP384SHA384 => match self {
144 #[cfg(feature = "openssl")]
145 Self::Der | Self::Pem => KeyPair::generate(algorithm)?,
146 #[cfg(feature = "ring")]
147 Self::Pkcs8 => return KeyPair::generate_pkcs8(algorithm),
148 e => return Err(format!("unsupported key format with EC: {e:?}").into()),
149 },
150 #[cfg(feature = "ring")]
151 Algorithm::ED25519 => return KeyPair::generate_pkcs8(algorithm),
152 e => {
153 return Err(
154 format!("unsupported Algorithm, enable openssl or ring feature: {e:?}").into(),
155 )
156 }
157 };
158
159 #[allow(unreachable_code)]
161 match key_pair {
162 #[cfg(feature = "openssl")]
163 KeyPair::EC(ref pkey) | KeyPair::RSA(ref pkey) => {
164 match self {
165 Self::Der => {
166 if password.is_some() {
168 return Err(format!("Can only password protect PEM: {self:?}").into());
169 }
170 pkey.private_key_to_der()
171 .map_err(|e| format!("error writing key as DER: {e}").into())
172 }
173 Self::Pem => {
174 let key = if let Some(password) = password {
175 pkey.private_key_to_pem_pkcs8_passphrase(
176 Cipher::aes_256_cbc(),
177 password,
178 )
179 } else {
180 pkey.private_key_to_pem_pkcs8()
181 };
182
183 key.map_err(|e| format!("error writing key as PEM: {e}").into())
184 }
185 e => Err(format!(
186 "unsupported key format with RSA or EC (DER or PEM \
187 only): {e:?}"
188 )
189 .into()),
190 }
191 }
192 #[cfg(feature = "ring")]
193 KeyPair::ECDSA(..) | KeyPair::ED25519(..) => panic!("should have returned early"),
194 #[cfg(not(feature = "openssl"))]
195 KeyPair::Phantom(..) => panic!("Phantom disallowed"),
196 #[cfg(not(any(feature = "openssl", feature = "ring")))]
197 _ => Err(format!(
198 "unsupported Algorithm, enable openssl feature (encode not supported with ring)"
199 )
200 .into()),
201 }
202 }
203
204 #[deprecated]
206 pub fn encode_key(
207 self,
208 key_pair: &KeyPair<Private>,
209 password: Option<&str>,
210 ) -> DnsSecResult<Vec<u8>> {
211 #[allow(unused)]
213 let password = password
214 .iter()
215 .filter(|s| !s.is_empty())
216 .map(|s| s.as_bytes())
217 .next();
218
219 match *key_pair {
220 #[cfg(feature = "openssl")]
221 KeyPair::EC(ref pkey) | KeyPair::RSA(ref pkey) => {
222 match self {
223 Self::Der => {
224 if password.is_some() {
226 return Err(format!("Can only password protect PEM: {self:?}").into());
227 }
228 pkey.private_key_to_der()
229 .map_err(|e| format!("error writing key as DER: {e}").into())
230 }
231 Self::Pem => {
232 let key = if let Some(password) = password {
233 pkey.private_key_to_pem_pkcs8_passphrase(
234 Cipher::aes_256_cbc(),
235 password,
236 )
237 } else {
238 pkey.private_key_to_pem_pkcs8()
239 };
240
241 key.map_err(|e| format!("error writing key as PEM: {e}").into())
242 }
243 e => Err(format!(
244 "unsupported key format with RSA or EC (DER or PEM \
245 only): {e:?}"
246 )
247 .into()),
248 }
249 }
250 #[cfg(any(feature = "ring", not(feature = "openssl")))]
251 _ => Err(
252 "unsupported Algorithm, enable openssl feature (encode not supported with ring)"
253 .into(),
254 ),
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 #![allow(clippy::dbg_macro, clippy::print_stdout)]
262
263 use super::*;
264
265 #[test]
266 #[cfg(feature = "openssl")]
267 fn test_rsa_encode_decode_der() {
268 let algorithm = Algorithm::RSASHA256;
269 encode_decode_with_format(KeyFormat::Der, algorithm, false, true);
270 }
271
272 #[test]
273 #[cfg(feature = "openssl")]
274 fn test_rsa_encode_decode_pem() {
275 let algorithm = Algorithm::RSASHA256;
276 encode_decode_with_format(KeyFormat::Pem, algorithm, true, true);
277 }
278
279 #[test]
280 #[cfg(feature = "openssl")]
281 fn test_ec_encode_decode_der() {
282 let algorithm = Algorithm::ECDSAP256SHA256;
283 encode_decode_with_format(KeyFormat::Der, algorithm, false, true);
284 }
285
286 #[test]
287 #[cfg(feature = "openssl")]
288 fn test_ec_encode_decode_pem() {
289 let algorithm = Algorithm::ECDSAP256SHA256;
290 encode_decode_with_format(KeyFormat::Pem, algorithm, true, true);
291 }
292
293 #[test]
294 #[cfg(feature = "ring")]
295 fn test_ec_encode_decode_pkcs8() {
296 let algorithm = Algorithm::ECDSAP256SHA256;
297 encode_decode_with_format(KeyFormat::Pkcs8, algorithm, true, true);
298 }
299
300 #[test]
301 #[cfg(feature = "ring")]
302 fn test_ed25519_encode_decode_pkcs8() {
303 let algorithm = Algorithm::ED25519;
304 encode_decode_with_format(KeyFormat::Pkcs8, algorithm, true, true);
305 }
306
307 #[cfg(test)]
308 fn encode_decode_with_format(
309 key_format: KeyFormat,
310 algorithm: Algorithm,
311 ok_pass: bool,
312 ok_empty_pass: bool,
313 ) {
314 let password = Some("test password");
315 let empty_password = Some("");
316 let no_password = None::<&str>;
317
318 encode_decode_with_password(key_format, password, password, algorithm, ok_pass, true);
319 encode_decode_with_password(
320 key_format,
321 empty_password,
322 empty_password,
323 algorithm,
324 ok_empty_pass,
325 true,
326 );
327 encode_decode_with_password(
328 key_format,
329 no_password,
330 no_password,
331 algorithm,
332 ok_empty_pass,
333 true,
334 );
335 encode_decode_with_password(
336 key_format,
337 no_password,
338 empty_password,
339 algorithm,
340 ok_empty_pass,
341 true,
342 );
343 encode_decode_with_password(
344 key_format,
345 empty_password,
346 no_password,
347 algorithm,
348 ok_empty_pass,
349 true,
350 );
351 }
359
360 #[cfg(test)]
361 fn encode_decode_with_password(
362 key_format: KeyFormat,
363 en_pass: Option<&str>,
364 de_pass: Option<&str>,
365 algorithm: Algorithm,
366 encode: bool,
367 decode: bool,
368 ) {
369 println!(
370 "test params: format: {key_format:?}, en_pass: {en_pass:?}, de_pass: {de_pass:?}, alg: {algorithm:?}, encode: {encode}, decode: {decode}"
371 );
372 let encoded_rslt = key_format.generate_and_encode(algorithm, en_pass);
373
374 if encode {
375 let encoded = encoded_rslt.expect("Encoding error");
376 let decoded = key_format.decode_key(&encoded, de_pass, algorithm);
377 assert_eq!(decoded.is_ok(), decode);
378 } else {
379 assert!(encoded_rslt.is_err());
380 }
381 }
382}