1#[cfg(feature = "alloc")]
4mod name;
5
6use crate::{Error, Result};
7use core::{fmt, str};
8use encoding::{Label, LabelError};
9use sha2::{Digest, Sha256, Sha512};
10
11#[cfg(feature = "alloc")]
12use alloc::{borrow::ToOwned, string::String, vec::Vec};
13
14#[cfg(feature = "alloc")]
15pub use name::AlgorithmName;
16
17const BCRYPT: &str = "bcrypt";
19
20const CERT_DSA: &str = "ssh-dss-cert-v01@openssh.com";
22
23const CERT_ECDSA_SHA2_P256: &str = "ecdsa-sha2-nistp256-cert-v01@openssh.com";
25
26const CERT_ECDSA_SHA2_P384: &str = "ecdsa-sha2-nistp384-cert-v01@openssh.com";
28
29const CERT_ECDSA_SHA2_P521: &str = "ecdsa-sha2-nistp521-cert-v01@openssh.com";
31
32const CERT_ED25519: &str = "ssh-ed25519-cert-v01@openssh.com";
34
35const CERT_RSA: &str = "ssh-rsa-cert-v01@openssh.com";
37
38const CERT_RSA_SHA2_256: &str = "rsa-sha2-256-cert-v01@openssh.com";
40
41const CERT_RSA_SHA2_512: &str = "rsa-sha2-512-cert-v01@openssh.com";
43
44const CERT_SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com";
46
47const CERT_SK_SSH_ED25519: &str = "sk-ssh-ed25519-cert-v01@openssh.com";
49
50const ECDSA_SHA2_P256: &str = "ecdsa-sha2-nistp256";
52
53const ECDSA_SHA2_P384: &str = "ecdsa-sha2-nistp384";
55
56const ECDSA_SHA2_P521: &str = "ecdsa-sha2-nistp521";
58
59const NONE: &str = "none";
61
62const RSA_SHA2_256: &str = "rsa-sha2-256";
64
65const RSA_SHA2_512: &str = "rsa-sha2-512";
67
68const SHA256: &str = "sha256";
70
71const SHA512: &str = "sha512";
73
74const SSH_DSA: &str = "ssh-dss";
76
77const SSH_ED25519: &str = "ssh-ed25519";
79
80const SSH_RSA: &str = "ssh-rsa";
82
83const SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256@openssh.com";
85
86const SK_SSH_ED25519: &str = "sk-ssh-ed25519@openssh.com";
88
89#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
91#[non_exhaustive]
92pub enum Algorithm {
93 Dsa,
95
96 Ecdsa {
98 curve: EcdsaCurve,
100 },
101
102 #[default]
104 Ed25519,
105
106 Rsa {
108 hash: Option<HashAlg>,
116 },
117
118 SkEcdsaSha2NistP256,
120
121 SkEd25519,
123
124 #[cfg(feature = "alloc")]
126 Other(AlgorithmName),
127}
128
129impl Algorithm {
130 pub fn new(id: &str) -> Result<Self> {
147 Ok(id.parse()?)
148 }
149
150 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 #[must_use]
202 pub fn as_str(&self) -> &str {
203 match self {
204 Algorithm::Dsa => SSH_DSA,
205 Algorithm::Ecdsa { curve } => match curve {
206 EcdsaCurve::NistP256 => ECDSA_SHA2_P256,
207 EcdsaCurve::NistP384 => ECDSA_SHA2_P384,
208 EcdsaCurve::NistP521 => ECDSA_SHA2_P521,
209 },
210 Algorithm::Ed25519 => SSH_ED25519,
211 Algorithm::Rsa { hash } => match hash {
212 None => SSH_RSA,
213 Some(HashAlg::Sha256) => RSA_SHA2_256,
214 Some(HashAlg::Sha512) => RSA_SHA2_512,
215 },
216 Algorithm::SkEcdsaSha2NistP256 => SK_ECDSA_SHA2_P256,
217 Algorithm::SkEd25519 => SK_SSH_ED25519,
218 #[cfg(feature = "alloc")]
219 Algorithm::Other(algorithm) => algorithm.as_str(),
220 }
221 }
222
223 #[cfg(feature = "alloc")]
231 #[must_use]
232 pub fn to_certificate_type(&self) -> String {
233 match self {
234 Algorithm::Dsa => CERT_DSA,
235 Algorithm::Ecdsa { curve } => match curve {
236 EcdsaCurve::NistP256 => CERT_ECDSA_SHA2_P256,
237 EcdsaCurve::NistP384 => CERT_ECDSA_SHA2_P384,
238 EcdsaCurve::NistP521 => CERT_ECDSA_SHA2_P521,
239 },
240 Algorithm::Ed25519 => CERT_ED25519,
241 Algorithm::Rsa { hash: None } => CERT_RSA,
242 Algorithm::Rsa {
243 hash: Some(HashAlg::Sha256),
244 } => CERT_RSA_SHA2_256,
245 Algorithm::Rsa {
246 hash: Some(HashAlg::Sha512),
247 } => CERT_RSA_SHA2_512,
248 Algorithm::SkEcdsaSha2NistP256 => CERT_SK_ECDSA_SHA2_P256,
249 Algorithm::SkEd25519 => CERT_SK_SSH_ED25519,
250 Algorithm::Other(algorithm) => return algorithm.certificate_type(),
251 }
252 .to_owned()
253 }
254
255 #[must_use]
257 pub fn is_dsa(self) -> bool {
258 self == Algorithm::Dsa
259 }
260
261 #[must_use]
263 pub fn is_ecdsa(self) -> bool {
264 matches!(self, Algorithm::Ecdsa { .. })
265 }
266
267 #[must_use]
269 pub fn is_ed25519(self) -> bool {
270 self == Algorithm::Ed25519
271 }
272
273 #[must_use]
275 pub fn is_rsa(self) -> bool {
276 matches!(self, Algorithm::Rsa { .. })
277 }
278
279 #[allow(dead_code)]
281 pub(crate) fn unsupported_error(self) -> Error {
282 Error::AlgorithmUnsupported { algorithm: self }
283 }
284}
285
286impl AsRef<str> for Algorithm {
287 fn as_ref(&self) -> &str {
288 self.as_str()
289 }
290}
291
292impl Label for Algorithm {}
293
294impl fmt::Display for Algorithm {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 f.write_str(self.as_str())
297 }
298}
299
300impl str::FromStr for Algorithm {
301 type Err = LabelError;
302
303 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
304 match id {
305 SSH_DSA => Ok(Algorithm::Dsa),
306 ECDSA_SHA2_P256 => Ok(Algorithm::Ecdsa {
307 curve: EcdsaCurve::NistP256,
308 }),
309 ECDSA_SHA2_P384 => Ok(Algorithm::Ecdsa {
310 curve: EcdsaCurve::NistP384,
311 }),
312 ECDSA_SHA2_P521 => Ok(Algorithm::Ecdsa {
313 curve: EcdsaCurve::NistP521,
314 }),
315 RSA_SHA2_256 => Ok(Algorithm::Rsa {
316 hash: Some(HashAlg::Sha256),
317 }),
318 RSA_SHA2_512 => Ok(Algorithm::Rsa {
319 hash: Some(HashAlg::Sha512),
320 }),
321 SSH_ED25519 => Ok(Algorithm::Ed25519),
322 SSH_RSA => Ok(Algorithm::Rsa { hash: None }),
323 SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
324 SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
325 #[cfg(feature = "alloc")]
326 _ => Ok(Algorithm::Other(AlgorithmName::from_str(id)?)),
327 #[cfg(not(feature = "alloc"))]
328 _ => Err(LabelError::new(id)),
329 }
330 }
331}
332
333#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
335pub enum EcdsaCurve {
336 NistP256,
338
339 NistP384,
341
342 NistP521,
344}
345
346impl EcdsaCurve {
347 pub fn new(id: &str) -> Result<Self> {
358 Ok(id.parse()?)
359 }
360
361 #[must_use]
363 pub fn as_str(self) -> &'static str {
364 match self {
365 EcdsaCurve::NistP256 => "nistp256",
366 EcdsaCurve::NistP384 => "nistp384",
367 EcdsaCurve::NistP521 => "nistp521",
368 }
369 }
370
371 #[cfg(feature = "alloc")]
373 pub(crate) const fn field_size(self) -> usize {
374 match self {
375 EcdsaCurve::NistP256 => 32,
376 EcdsaCurve::NistP384 => 48,
377 EcdsaCurve::NistP521 => 66,
378 }
379 }
380}
381
382impl AsRef<str> for EcdsaCurve {
383 fn as_ref(&self) -> &str {
384 self.as_str()
385 }
386}
387
388impl From<EcdsaCurve> for Algorithm {
389 fn from(curve: EcdsaCurve) -> Algorithm {
390 Algorithm::Ecdsa { curve }
391 }
392}
393
394impl Label for EcdsaCurve {}
395
396impl fmt::Display for EcdsaCurve {
397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398 f.write_str(self.as_str())
399 }
400}
401
402impl str::FromStr for EcdsaCurve {
403 type Err = LabelError;
404
405 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
406 match id {
407 "nistp256" => Ok(EcdsaCurve::NistP256),
408 "nistp384" => Ok(EcdsaCurve::NistP384),
409 "nistp521" => Ok(EcdsaCurve::NistP521),
410 _ => Err(LabelError::new(id)),
411 }
412 }
413}
414
415#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
417#[non_exhaustive]
418pub enum HashAlg {
419 #[default]
421 Sha256,
422
423 Sha512,
425}
426
427impl HashAlg {
428 pub fn new(id: &str) -> Result<Self> {
438 Ok(id.parse()?)
439 }
440
441 #[must_use]
443 pub fn as_str(self) -> &'static str {
444 match self {
445 HashAlg::Sha256 => SHA256,
446 HashAlg::Sha512 => SHA512,
447 }
448 }
449
450 #[must_use]
452 pub const fn digest_size(self) -> usize {
453 match self {
454 HashAlg::Sha256 => 32,
455 HashAlg::Sha512 => 64,
456 }
457 }
458
459 #[cfg(feature = "alloc")]
461 #[must_use]
462 pub fn digest(self, msg: &[u8]) -> Vec<u8> {
463 match self {
464 HashAlg::Sha256 => Sha256::digest(msg).to_vec(),
465 HashAlg::Sha512 => Sha512::digest(msg).to_vec(),
466 }
467 }
468}
469
470impl Label for HashAlg {}
471
472impl AsRef<str> for HashAlg {
473 fn as_ref(&self) -> &str {
474 self.as_str()
475 }
476}
477
478impl fmt::Display for HashAlg {
479 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
480 f.write_str(self.as_str())
481 }
482}
483
484impl str::FromStr for HashAlg {
485 type Err = LabelError;
486
487 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
488 match id {
489 SHA256 => Ok(HashAlg::Sha256),
490 SHA512 => Ok(HashAlg::Sha512),
491 _ => Err(LabelError::new(id)),
492 }
493 }
494}
495
496pub trait AssociatedHashAlg: Digest {
498 const HASH_ALG: HashAlg;
500}
501
502impl AssociatedHashAlg for Sha256 {
503 const HASH_ALG: HashAlg = HashAlg::Sha256;
504}
505
506impl AssociatedHashAlg for Sha512 {
507 const HASH_ALG: HashAlg = HashAlg::Sha512;
508}
509
510#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
512#[non_exhaustive]
513pub enum KdfAlg {
514 None,
516
517 #[default]
519 Bcrypt,
520}
521
522impl KdfAlg {
523 pub fn new(kdfname: &str) -> Result<Self> {
532 Ok(kdfname.parse()?)
533 }
534
535 #[must_use]
537 pub fn as_str(self) -> &'static str {
538 match self {
539 Self::None => NONE,
540 Self::Bcrypt => BCRYPT,
541 }
542 }
543
544 #[must_use]
546 pub fn is_none(self) -> bool {
547 self == Self::None
548 }
549}
550
551impl Label for KdfAlg {}
552
553impl AsRef<str> for KdfAlg {
554 fn as_ref(&self) -> &str {
555 self.as_str()
556 }
557}
558
559impl fmt::Display for KdfAlg {
560 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561 f.write_str(self.as_str())
562 }
563}
564
565impl str::FromStr for KdfAlg {
566 type Err = LabelError;
567
568 fn from_str(kdfname: &str) -> core::result::Result<Self, LabelError> {
569 match kdfname {
570 NONE => Ok(Self::None),
571 BCRYPT => Ok(Self::Bcrypt),
572 _ => Err(LabelError::new(kdfname)),
573 }
574 }
575}