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> {
144 Ok(id.parse()?)
145 }
146
147 pub fn new_certificate(id: &str) -> Result<Self> {
167 match id {
168 CERT_DSA => Ok(Algorithm::Dsa),
169 CERT_ECDSA_SHA2_P256 => Ok(Algorithm::Ecdsa {
170 curve: EcdsaCurve::NistP256,
171 }),
172 CERT_ECDSA_SHA2_P384 => Ok(Algorithm::Ecdsa {
173 curve: EcdsaCurve::NistP384,
174 }),
175 CERT_ECDSA_SHA2_P521 => Ok(Algorithm::Ecdsa {
176 curve: EcdsaCurve::NistP521,
177 }),
178 CERT_ED25519 => Ok(Algorithm::Ed25519),
179 CERT_RSA => Ok(Algorithm::Rsa { hash: None }),
180 CERT_RSA_SHA2_256 => Ok(Algorithm::Rsa {
181 hash: Some(HashAlg::Sha256),
182 }),
183 CERT_RSA_SHA2_512 => Ok(Algorithm::Rsa {
184 hash: Some(HashAlg::Sha512),
185 }),
186 CERT_SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
187 CERT_SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
188 #[cfg(feature = "alloc")]
189 _ => Ok(Algorithm::Other(AlgorithmName::from_certificate_type(id)?)),
190 #[cfg(not(feature = "alloc"))]
191 _ => Err(Error::AlgorithmUnknown),
192 }
193 }
194
195 pub fn as_str(&self) -> &str {
197 match self {
198 Algorithm::Dsa => SSH_DSA,
199 Algorithm::Ecdsa { curve } => match curve {
200 EcdsaCurve::NistP256 => ECDSA_SHA2_P256,
201 EcdsaCurve::NistP384 => ECDSA_SHA2_P384,
202 EcdsaCurve::NistP521 => ECDSA_SHA2_P521,
203 },
204 Algorithm::Ed25519 => SSH_ED25519,
205 Algorithm::Rsa { hash } => match hash {
206 None => SSH_RSA,
207 Some(HashAlg::Sha256) => RSA_SHA2_256,
208 Some(HashAlg::Sha512) => RSA_SHA2_512,
209 },
210 Algorithm::SkEcdsaSha2NistP256 => SK_ECDSA_SHA2_P256,
211 Algorithm::SkEd25519 => SK_SSH_ED25519,
212 #[cfg(feature = "alloc")]
213 Algorithm::Other(algorithm) => algorithm.as_str(),
214 }
215 }
216
217 #[cfg(feature = "alloc")]
225 pub fn to_certificate_type(&self) -> String {
226 match self {
227 Algorithm::Dsa => CERT_DSA,
228 Algorithm::Ecdsa { curve } => match curve {
229 EcdsaCurve::NistP256 => CERT_ECDSA_SHA2_P256,
230 EcdsaCurve::NistP384 => CERT_ECDSA_SHA2_P384,
231 EcdsaCurve::NistP521 => CERT_ECDSA_SHA2_P521,
232 },
233 Algorithm::Ed25519 => CERT_ED25519,
234 Algorithm::Rsa { hash: None } => CERT_RSA,
235 Algorithm::Rsa {
236 hash: Some(HashAlg::Sha256),
237 } => CERT_RSA_SHA2_256,
238 Algorithm::Rsa {
239 hash: Some(HashAlg::Sha512),
240 } => CERT_RSA_SHA2_512,
241 Algorithm::SkEcdsaSha2NistP256 => CERT_SK_ECDSA_SHA2_P256,
242 Algorithm::SkEd25519 => CERT_SK_SSH_ED25519,
243 Algorithm::Other(algorithm) => return algorithm.certificate_type(),
244 }
245 .to_owned()
246 }
247
248 pub fn is_dsa(self) -> bool {
250 self == Algorithm::Dsa
251 }
252
253 pub fn is_ecdsa(self) -> bool {
255 matches!(self, Algorithm::Ecdsa { .. })
256 }
257
258 pub fn is_ed25519(self) -> bool {
260 self == Algorithm::Ed25519
261 }
262
263 pub fn is_rsa(self) -> bool {
265 matches!(self, Algorithm::Rsa { .. })
266 }
267
268 #[allow(dead_code)]
270 pub(crate) fn unsupported_error(self) -> Error {
271 Error::AlgorithmUnsupported { algorithm: self }
272 }
273}
274
275impl AsRef<str> for Algorithm {
276 fn as_ref(&self) -> &str {
277 self.as_str()
278 }
279}
280
281impl Label for Algorithm {}
282
283impl fmt::Display for Algorithm {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 f.write_str(self.as_str())
286 }
287}
288
289impl str::FromStr for Algorithm {
290 type Err = LabelError;
291
292 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
293 match id {
294 SSH_DSA => Ok(Algorithm::Dsa),
295 ECDSA_SHA2_P256 => Ok(Algorithm::Ecdsa {
296 curve: EcdsaCurve::NistP256,
297 }),
298 ECDSA_SHA2_P384 => Ok(Algorithm::Ecdsa {
299 curve: EcdsaCurve::NistP384,
300 }),
301 ECDSA_SHA2_P521 => Ok(Algorithm::Ecdsa {
302 curve: EcdsaCurve::NistP521,
303 }),
304 RSA_SHA2_256 => Ok(Algorithm::Rsa {
305 hash: Some(HashAlg::Sha256),
306 }),
307 RSA_SHA2_512 => Ok(Algorithm::Rsa {
308 hash: Some(HashAlg::Sha512),
309 }),
310 SSH_ED25519 => Ok(Algorithm::Ed25519),
311 SSH_RSA => Ok(Algorithm::Rsa { hash: None }),
312 SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
313 SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
314 #[cfg(feature = "alloc")]
315 _ => Ok(Algorithm::Other(AlgorithmName::from_str(id)?)),
316 #[cfg(not(feature = "alloc"))]
317 _ => Err(LabelError::new(id)),
318 }
319 }
320}
321
322#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
324pub enum EcdsaCurve {
325 NistP256,
327
328 NistP384,
330
331 NistP521,
333}
334
335impl EcdsaCurve {
336 pub fn new(id: &str) -> Result<Self> {
344 Ok(id.parse()?)
345 }
346
347 pub fn as_str(self) -> &'static str {
349 match self {
350 EcdsaCurve::NistP256 => "nistp256",
351 EcdsaCurve::NistP384 => "nistp384",
352 EcdsaCurve::NistP521 => "nistp521",
353 }
354 }
355
356 #[cfg(feature = "alloc")]
358 pub(crate) const fn field_size(self) -> usize {
359 match self {
360 EcdsaCurve::NistP256 => 32,
361 EcdsaCurve::NistP384 => 48,
362 EcdsaCurve::NistP521 => 66,
363 }
364 }
365}
366
367impl AsRef<str> for EcdsaCurve {
368 fn as_ref(&self) -> &str {
369 self.as_str()
370 }
371}
372
373impl Label for EcdsaCurve {}
374
375impl fmt::Display for EcdsaCurve {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 f.write_str(self.as_str())
378 }
379}
380
381impl str::FromStr for EcdsaCurve {
382 type Err = LabelError;
383
384 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
385 match id {
386 "nistp256" => Ok(EcdsaCurve::NistP256),
387 "nistp384" => Ok(EcdsaCurve::NistP384),
388 "nistp521" => Ok(EcdsaCurve::NistP521),
389 _ => Err(LabelError::new(id)),
390 }
391 }
392}
393
394#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
396#[non_exhaustive]
397pub enum HashAlg {
398 #[default]
400 Sha256,
401
402 Sha512,
404}
405
406impl HashAlg {
407 pub fn new(id: &str) -> Result<Self> {
414 Ok(id.parse()?)
415 }
416
417 pub fn as_str(self) -> &'static str {
419 match self {
420 HashAlg::Sha256 => SHA256,
421 HashAlg::Sha512 => SHA512,
422 }
423 }
424
425 pub const fn digest_size(self) -> usize {
427 match self {
428 HashAlg::Sha256 => 32,
429 HashAlg::Sha512 => 64,
430 }
431 }
432
433 #[cfg(feature = "alloc")]
435 pub fn digest(self, msg: &[u8]) -> Vec<u8> {
436 match self {
437 HashAlg::Sha256 => Sha256::digest(msg).to_vec(),
438 HashAlg::Sha512 => Sha512::digest(msg).to_vec(),
439 }
440 }
441}
442
443impl Label for HashAlg {}
444
445impl AsRef<str> for HashAlg {
446 fn as_ref(&self) -> &str {
447 self.as_str()
448 }
449}
450
451impl fmt::Display for HashAlg {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 f.write_str(self.as_str())
454 }
455}
456
457impl str::FromStr for HashAlg {
458 type Err = LabelError;
459
460 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
461 match id {
462 SHA256 => Ok(HashAlg::Sha256),
463 SHA512 => Ok(HashAlg::Sha512),
464 _ => Err(LabelError::new(id)),
465 }
466 }
467}
468
469pub trait AssociatedHashAlg: Digest {
471 const HASH_ALG: HashAlg;
473}
474
475impl AssociatedHashAlg for Sha256 {
476 const HASH_ALG: HashAlg = HashAlg::Sha256;
477}
478
479impl AssociatedHashAlg for Sha512 {
480 const HASH_ALG: HashAlg = HashAlg::Sha512;
481}
482
483#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
485#[non_exhaustive]
486pub enum KdfAlg {
487 None,
489
490 #[default]
492 Bcrypt,
493}
494
495impl KdfAlg {
496 pub fn new(kdfname: &str) -> Result<Self> {
501 Ok(kdfname.parse()?)
502 }
503
504 pub fn as_str(self) -> &'static str {
506 match self {
507 Self::None => NONE,
508 Self::Bcrypt => BCRYPT,
509 }
510 }
511
512 pub fn is_none(self) -> bool {
514 self == Self::None
515 }
516}
517
518impl Label for KdfAlg {}
519
520impl AsRef<str> for KdfAlg {
521 fn as_ref(&self) -> &str {
522 self.as_str()
523 }
524}
525
526impl fmt::Display for KdfAlg {
527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528 f.write_str(self.as_str())
529 }
530}
531
532impl str::FromStr for KdfAlg {
533 type Err = LabelError;
534
535 fn from_str(kdfname: &str) -> core::result::Result<Self, LabelError> {
536 match kdfname {
537 NONE => Ok(Self::None),
538 BCRYPT => Ok(Self::Bcrypt),
539 _ => Err(LabelError::new(kdfname)),
540 }
541 }
542}