1#[cfg(feature = "alloc")]
4mod name;
5
6use crate::{Error, Result};
7use core::{fmt, str};
8use encoding::{Label, LabelError};
9
10#[cfg(feature = "alloc")]
11use {
12 alloc::{borrow::ToOwned, string::String, vec::Vec},
13 sha2::{Digest, Sha256, Sha512},
14};
15
16#[cfg(feature = "alloc")]
17pub use name::AlgorithmName;
18
19const BCRYPT: &str = "bcrypt";
21
22const CERT_DSA: &str = "ssh-dss-cert-v01@openssh.com";
24
25const CERT_ECDSA_SHA2_P256: &str = "ecdsa-sha2-nistp256-cert-v01@openssh.com";
27
28const CERT_ECDSA_SHA2_P384: &str = "ecdsa-sha2-nistp384-cert-v01@openssh.com";
30
31const CERT_ECDSA_SHA2_P521: &str = "ecdsa-sha2-nistp521-cert-v01@openssh.com";
33
34const CERT_ED25519: &str = "ssh-ed25519-cert-v01@openssh.com";
36
37const CERT_RSA: &str = "ssh-rsa-cert-v01@openssh.com";
39
40const CERT_RSA_SHA2_256: &str = "rsa-sha2-256-cert-v01@openssh.com";
42
43const CERT_RSA_SHA2_512: &str = "rsa-sha2-512-cert-v01@openssh.com";
45
46const CERT_SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com";
48
49const CERT_SK_SSH_ED25519: &str = "sk-ssh-ed25519-cert-v01@openssh.com";
51
52const ECDSA_SHA2_P256: &str = "ecdsa-sha2-nistp256";
54
55const ECDSA_SHA2_P384: &str = "ecdsa-sha2-nistp384";
57
58const ECDSA_SHA2_P521: &str = "ecdsa-sha2-nistp521";
60
61const NONE: &str = "none";
63
64const RSA_SHA2_256: &str = "rsa-sha2-256";
66
67const RSA_SHA2_512: &str = "rsa-sha2-512";
69
70const SHA256: &str = "sha256";
72
73const SHA512: &str = "sha512";
75
76const SSH_DSA: &str = "ssh-dss";
78
79const SSH_ED25519: &str = "ssh-ed25519";
81
82const SSH_RSA: &str = "ssh-rsa";
84
85const SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256@openssh.com";
87
88const SK_SSH_ED25519: &str = "sk-ssh-ed25519@openssh.com";
90
91#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
96#[non_exhaustive]
97pub enum Algorithm {
98 Dsa,
100
101 Ecdsa {
103 curve: EcdsaCurve,
105 },
106
107 #[default]
109 Ed25519,
110
111 Rsa {
113 hash: Option<HashAlg>,
121 },
122
123 SkEcdsaSha2NistP256,
125
126 SkEd25519,
128
129 #[cfg(feature = "alloc")]
131 Other(AlgorithmName),
132}
133
134impl Algorithm {
135 pub fn new(id: &str) -> Result<Self> {
149 Ok(id.parse()?)
150 }
151
152 pub fn new_certificate(id: &str) -> Result<Self> {
172 match id {
173 CERT_DSA => Ok(Algorithm::Dsa),
174 CERT_ECDSA_SHA2_P256 => Ok(Algorithm::Ecdsa {
175 curve: EcdsaCurve::NistP256,
176 }),
177 CERT_ECDSA_SHA2_P384 => Ok(Algorithm::Ecdsa {
178 curve: EcdsaCurve::NistP384,
179 }),
180 CERT_ECDSA_SHA2_P521 => Ok(Algorithm::Ecdsa {
181 curve: EcdsaCurve::NistP521,
182 }),
183 CERT_ED25519 => Ok(Algorithm::Ed25519),
184 CERT_RSA => Ok(Algorithm::Rsa { hash: None }),
185 CERT_RSA_SHA2_256 => Ok(Algorithm::Rsa {
186 hash: Some(HashAlg::Sha256),
187 }),
188 CERT_RSA_SHA2_512 => Ok(Algorithm::Rsa {
189 hash: Some(HashAlg::Sha512),
190 }),
191 CERT_SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
192 CERT_SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
193 #[cfg(feature = "alloc")]
194 _ => Ok(Algorithm::Other(AlgorithmName::from_certificate_type(id)?)),
195 #[cfg(not(feature = "alloc"))]
196 _ => Err(Error::AlgorithmUnknown),
197 }
198 }
199
200 pub fn as_str(&self) -> &str {
202 match self {
203 Algorithm::Dsa => SSH_DSA,
204 Algorithm::Ecdsa { curve } => match curve {
205 EcdsaCurve::NistP256 => ECDSA_SHA2_P256,
206 EcdsaCurve::NistP384 => ECDSA_SHA2_P384,
207 EcdsaCurve::NistP521 => ECDSA_SHA2_P521,
208 },
209 Algorithm::Ed25519 => SSH_ED25519,
210 Algorithm::Rsa { hash } => match hash {
211 None => SSH_RSA,
212 Some(HashAlg::Sha256) => RSA_SHA2_256,
213 Some(HashAlg::Sha512) => RSA_SHA2_512,
214 },
215 Algorithm::SkEcdsaSha2NistP256 => SK_ECDSA_SHA2_P256,
216 Algorithm::SkEd25519 => SK_SSH_ED25519,
217 #[cfg(feature = "alloc")]
218 Algorithm::Other(algorithm) => algorithm.as_str(),
219 }
220 }
221
222 #[cfg(feature = "alloc")]
230 pub fn to_certificate_type(&self) -> String {
231 match self {
232 Algorithm::Dsa => CERT_DSA,
233 Algorithm::Ecdsa { curve } => match curve {
234 EcdsaCurve::NistP256 => CERT_ECDSA_SHA2_P256,
235 EcdsaCurve::NistP384 => CERT_ECDSA_SHA2_P384,
236 EcdsaCurve::NistP521 => CERT_ECDSA_SHA2_P521,
237 },
238 Algorithm::Ed25519 => CERT_ED25519,
239 Algorithm::Rsa { hash: None } => CERT_RSA,
240 Algorithm::Rsa {
241 hash: Some(HashAlg::Sha256),
242 } => CERT_RSA_SHA2_256,
243 Algorithm::Rsa {
244 hash: Some(HashAlg::Sha512),
245 } => CERT_RSA_SHA2_512,
246 Algorithm::SkEcdsaSha2NistP256 => CERT_SK_ECDSA_SHA2_P256,
247 Algorithm::SkEd25519 => CERT_SK_SSH_ED25519,
248 Algorithm::Other(algorithm) => return algorithm.certificate_type(),
249 }
250 .to_owned()
251 }
252
253 pub fn is_dsa(self) -> bool {
255 self == Algorithm::Dsa
256 }
257
258 pub fn is_ecdsa(self) -> bool {
260 matches!(self, Algorithm::Ecdsa { .. })
261 }
262
263 pub fn is_ed25519(self) -> bool {
265 self == Algorithm::Ed25519
266 }
267
268 pub fn is_rsa(self) -> bool {
270 matches!(self, Algorithm::Rsa { .. })
271 }
272
273 #[allow(dead_code)]
275 pub(crate) fn unsupported_error(self) -> Error {
276 Error::AlgorithmUnsupported { algorithm: self }
277 }
278}
279
280impl AsRef<str> for Algorithm {
281 fn as_ref(&self) -> &str {
282 self.as_str()
283 }
284}
285
286impl Label for Algorithm {}
287
288impl fmt::Display for Algorithm {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 f.write_str(self.as_str())
291 }
292}
293
294impl str::FromStr for Algorithm {
295 type Err = LabelError;
296
297 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
298 match id {
299 SSH_DSA => Ok(Algorithm::Dsa),
300 ECDSA_SHA2_P256 => Ok(Algorithm::Ecdsa {
301 curve: EcdsaCurve::NistP256,
302 }),
303 ECDSA_SHA2_P384 => Ok(Algorithm::Ecdsa {
304 curve: EcdsaCurve::NistP384,
305 }),
306 ECDSA_SHA2_P521 => Ok(Algorithm::Ecdsa {
307 curve: EcdsaCurve::NistP521,
308 }),
309 RSA_SHA2_256 => Ok(Algorithm::Rsa {
310 hash: Some(HashAlg::Sha256),
311 }),
312 RSA_SHA2_512 => Ok(Algorithm::Rsa {
313 hash: Some(HashAlg::Sha512),
314 }),
315 SSH_ED25519 => Ok(Algorithm::Ed25519),
316 SSH_RSA => Ok(Algorithm::Rsa { hash: None }),
317 SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
318 SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
319 #[cfg(feature = "alloc")]
320 _ => Ok(Algorithm::Other(AlgorithmName::from_str(id)?)),
321 #[cfg(not(feature = "alloc"))]
322 _ => Err(LabelError::new(id)),
323 }
324 }
325}
326
327#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
329pub enum EcdsaCurve {
330 NistP256,
332
333 NistP384,
335
336 NistP521,
338}
339
340impl EcdsaCurve {
341 pub fn new(id: &str) -> Result<Self> {
349 Ok(id.parse()?)
350 }
351
352 pub fn as_str(self) -> &'static str {
354 match self {
355 EcdsaCurve::NistP256 => "nistp256",
356 EcdsaCurve::NistP384 => "nistp384",
357 EcdsaCurve::NistP521 => "nistp521",
358 }
359 }
360
361 #[cfg(feature = "alloc")]
363 pub(crate) const fn field_size(self) -> usize {
364 match self {
365 EcdsaCurve::NistP256 => 32,
366 EcdsaCurve::NistP384 => 48,
367 EcdsaCurve::NistP521 => 66,
368 }
369 }
370}
371
372impl AsRef<str> for EcdsaCurve {
373 fn as_ref(&self) -> &str {
374 self.as_str()
375 }
376}
377
378impl Label for EcdsaCurve {}
379
380impl fmt::Display for EcdsaCurve {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 f.write_str(self.as_str())
383 }
384}
385
386impl str::FromStr for EcdsaCurve {
387 type Err = LabelError;
388
389 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
390 match id {
391 "nistp256" => Ok(EcdsaCurve::NistP256),
392 "nistp384" => Ok(EcdsaCurve::NistP384),
393 "nistp521" => Ok(EcdsaCurve::NistP521),
394 _ => Err(LabelError::new(id)),
395 }
396 }
397}
398
399#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
401#[non_exhaustive]
402pub enum HashAlg {
403 #[default]
405 Sha256,
406
407 Sha512,
409}
410
411impl HashAlg {
412 pub fn new(id: &str) -> Result<Self> {
419 Ok(id.parse()?)
420 }
421
422 pub fn as_str(self) -> &'static str {
424 match self {
425 HashAlg::Sha256 => SHA256,
426 HashAlg::Sha512 => SHA512,
427 }
428 }
429
430 pub const fn digest_size(self) -> usize {
432 match self {
433 HashAlg::Sha256 => 32,
434 HashAlg::Sha512 => 64,
435 }
436 }
437
438 #[cfg(feature = "alloc")]
440 pub fn digest(self, msg: &[u8]) -> Vec<u8> {
441 match self {
442 HashAlg::Sha256 => Sha256::digest(msg).to_vec(),
443 HashAlg::Sha512 => Sha512::digest(msg).to_vec(),
444 }
445 }
446}
447
448impl Label for HashAlg {}
449
450impl AsRef<str> for HashAlg {
451 fn as_ref(&self) -> &str {
452 self.as_str()
453 }
454}
455
456impl fmt::Display for HashAlg {
457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458 f.write_str(self.as_str())
459 }
460}
461
462impl str::FromStr for HashAlg {
463 type Err = LabelError;
464
465 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
466 match id {
467 SHA256 => Ok(HashAlg::Sha256),
468 SHA512 => Ok(HashAlg::Sha512),
469 _ => Err(LabelError::new(id)),
470 }
471 }
472}
473
474#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
476#[non_exhaustive]
477pub enum KdfAlg {
478 None,
480
481 #[default]
483 Bcrypt,
484}
485
486impl KdfAlg {
487 pub fn new(kdfname: &str) -> Result<Self> {
492 Ok(kdfname.parse()?)
493 }
494
495 pub fn as_str(self) -> &'static str {
497 match self {
498 Self::None => NONE,
499 Self::Bcrypt => BCRYPT,
500 }
501 }
502
503 pub fn is_none(self) -> bool {
505 self == Self::None
506 }
507}
508
509impl Label for KdfAlg {}
510
511impl AsRef<str> for KdfAlg {
512 fn as_ref(&self) -> &str {
513 self.as_str()
514 }
515}
516
517impl fmt::Display for KdfAlg {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 f.write_str(self.as_str())
520 }
521}
522
523impl str::FromStr for KdfAlg {
524 type Err = LabelError;
525
526 fn from_str(kdfname: &str) -> core::result::Result<Self, LabelError> {
527 match kdfname {
528 NONE => Ok(Self::None),
529 BCRYPT => Ok(Self::Bcrypt),
530 _ => Err(LabelError::new(kdfname)),
531 }
532 }
533}