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_SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com";
42
43const CERT_SK_SSH_ED25519: &str = "sk-ssh-ed25519-cert-v01@openssh.com";
45
46const ECDSA_SHA2_P256: &str = "ecdsa-sha2-nistp256";
48
49const ECDSA_SHA2_P384: &str = "ecdsa-sha2-nistp384";
51
52const ECDSA_SHA2_P521: &str = "ecdsa-sha2-nistp521";
54
55const NONE: &str = "none";
57
58const RSA_SHA2_256: &str = "rsa-sha2-256";
60
61const RSA_SHA2_512: &str = "rsa-sha2-512";
63
64const SHA256: &str = "sha256";
66
67const SHA512: &str = "sha512";
69
70const SSH_DSA: &str = "ssh-dss";
72
73const SSH_ED25519: &str = "ssh-ed25519";
75
76const SSH_RSA: &str = "ssh-rsa";
78
79const SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256@openssh.com";
81
82const SK_SSH_ED25519: &str = "sk-ssh-ed25519@openssh.com";
84
85#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
90#[non_exhaustive]
91pub enum Algorithm {
92 Dsa,
94
95 Ecdsa {
97 curve: EcdsaCurve,
99 },
100
101 #[default]
103 Ed25519,
104
105 Rsa {
107 hash: Option<HashAlg>,
115 },
116
117 SkEcdsaSha2NistP256,
119
120 SkEd25519,
122
123 #[cfg(feature = "alloc")]
125 Other(AlgorithmName),
126}
127
128impl Algorithm {
129 pub fn new(id: &str) -> Result<Self> {
143 Ok(id.parse()?)
144 }
145
146 pub fn new_certificate(id: &str) -> Result<Self> {
166 match id {
167 CERT_DSA => Ok(Algorithm::Dsa),
168 CERT_ECDSA_SHA2_P256 => Ok(Algorithm::Ecdsa {
169 curve: EcdsaCurve::NistP256,
170 }),
171 CERT_ECDSA_SHA2_P384 => Ok(Algorithm::Ecdsa {
172 curve: EcdsaCurve::NistP384,
173 }),
174 CERT_ECDSA_SHA2_P521 => Ok(Algorithm::Ecdsa {
175 curve: EcdsaCurve::NistP521,
176 }),
177 CERT_ED25519 => Ok(Algorithm::Ed25519),
178 CERT_RSA => Ok(Algorithm::Rsa { hash: None }),
179 CERT_SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
180 CERT_SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
181 #[cfg(feature = "alloc")]
182 _ => Ok(Algorithm::Other(AlgorithmName::from_certificate_type(id)?)),
183 #[cfg(not(feature = "alloc"))]
184 _ => Err(Error::AlgorithmUnknown),
185 }
186 }
187
188 pub fn as_str(&self) -> &str {
190 match self {
191 Algorithm::Dsa => SSH_DSA,
192 Algorithm::Ecdsa { curve } => match curve {
193 EcdsaCurve::NistP256 => ECDSA_SHA2_P256,
194 EcdsaCurve::NistP384 => ECDSA_SHA2_P384,
195 EcdsaCurve::NistP521 => ECDSA_SHA2_P521,
196 },
197 Algorithm::Ed25519 => SSH_ED25519,
198 Algorithm::Rsa { hash } => match hash {
199 None => SSH_RSA,
200 Some(HashAlg::Sha256) => RSA_SHA2_256,
201 Some(HashAlg::Sha512) => RSA_SHA2_512,
202 },
203 Algorithm::SkEcdsaSha2NistP256 => SK_ECDSA_SHA2_P256,
204 Algorithm::SkEd25519 => SK_SSH_ED25519,
205 #[cfg(feature = "alloc")]
206 Algorithm::Other(algorithm) => algorithm.as_str(),
207 }
208 }
209
210 #[cfg(feature = "alloc")]
218 pub fn to_certificate_type(&self) -> String {
219 match self {
220 Algorithm::Dsa => CERT_DSA,
221 Algorithm::Ecdsa { curve } => match curve {
222 EcdsaCurve::NistP256 => CERT_ECDSA_SHA2_P256,
223 EcdsaCurve::NistP384 => CERT_ECDSA_SHA2_P384,
224 EcdsaCurve::NistP521 => CERT_ECDSA_SHA2_P521,
225 },
226 Algorithm::Ed25519 => CERT_ED25519,
227 Algorithm::Rsa { .. } => CERT_RSA,
228 Algorithm::SkEcdsaSha2NistP256 => CERT_SK_ECDSA_SHA2_P256,
229 Algorithm::SkEd25519 => CERT_SK_SSH_ED25519,
230 Algorithm::Other(algorithm) => return algorithm.certificate_type(),
231 }
232 .to_owned()
233 }
234
235 pub fn is_dsa(self) -> bool {
237 self == Algorithm::Dsa
238 }
239
240 pub fn is_ecdsa(self) -> bool {
242 matches!(self, Algorithm::Ecdsa { .. })
243 }
244
245 pub fn is_ed25519(self) -> bool {
247 self == Algorithm::Ed25519
248 }
249
250 pub fn is_rsa(self) -> bool {
252 matches!(self, Algorithm::Rsa { .. })
253 }
254
255 #[allow(dead_code)]
257 pub(crate) fn unsupported_error(self) -> Error {
258 Error::AlgorithmUnsupported { algorithm: self }
259 }
260}
261
262impl AsRef<str> for Algorithm {
263 fn as_ref(&self) -> &str {
264 self.as_str()
265 }
266}
267
268impl Label for Algorithm {}
269
270impl fmt::Display for Algorithm {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 f.write_str(self.as_str())
273 }
274}
275
276impl str::FromStr for Algorithm {
277 type Err = LabelError;
278
279 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
280 match id {
281 SSH_DSA => Ok(Algorithm::Dsa),
282 ECDSA_SHA2_P256 => Ok(Algorithm::Ecdsa {
283 curve: EcdsaCurve::NistP256,
284 }),
285 ECDSA_SHA2_P384 => Ok(Algorithm::Ecdsa {
286 curve: EcdsaCurve::NistP384,
287 }),
288 ECDSA_SHA2_P521 => Ok(Algorithm::Ecdsa {
289 curve: EcdsaCurve::NistP521,
290 }),
291 RSA_SHA2_256 => Ok(Algorithm::Rsa {
292 hash: Some(HashAlg::Sha256),
293 }),
294 RSA_SHA2_512 => Ok(Algorithm::Rsa {
295 hash: Some(HashAlg::Sha512),
296 }),
297 SSH_ED25519 => Ok(Algorithm::Ed25519),
298 SSH_RSA => Ok(Algorithm::Rsa { hash: None }),
299 SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
300 SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
301 #[cfg(feature = "alloc")]
302 _ => Ok(Algorithm::Other(AlgorithmName::from_str(id)?)),
303 #[cfg(not(feature = "alloc"))]
304 _ => Err(LabelError::new(id)),
305 }
306 }
307}
308
309#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
311pub enum EcdsaCurve {
312 NistP256,
314
315 NistP384,
317
318 NistP521,
320}
321
322impl EcdsaCurve {
323 pub fn new(id: &str) -> Result<Self> {
331 Ok(id.parse()?)
332 }
333
334 pub fn as_str(self) -> &'static str {
336 match self {
337 EcdsaCurve::NistP256 => "nistp256",
338 EcdsaCurve::NistP384 => "nistp384",
339 EcdsaCurve::NistP521 => "nistp521",
340 }
341 }
342
343 #[cfg(feature = "alloc")]
345 pub(crate) const fn field_size(self) -> usize {
346 match self {
347 EcdsaCurve::NistP256 => 32,
348 EcdsaCurve::NistP384 => 48,
349 EcdsaCurve::NistP521 => 66,
350 }
351 }
352}
353
354impl AsRef<str> for EcdsaCurve {
355 fn as_ref(&self) -> &str {
356 self.as_str()
357 }
358}
359
360impl Label for EcdsaCurve {}
361
362impl fmt::Display for EcdsaCurve {
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364 f.write_str(self.as_str())
365 }
366}
367
368impl str::FromStr for EcdsaCurve {
369 type Err = LabelError;
370
371 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
372 match id {
373 "nistp256" => Ok(EcdsaCurve::NistP256),
374 "nistp384" => Ok(EcdsaCurve::NistP384),
375 "nistp521" => Ok(EcdsaCurve::NistP521),
376 _ => Err(LabelError::new(id)),
377 }
378 }
379}
380
381#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
383#[non_exhaustive]
384pub enum HashAlg {
385 #[default]
387 Sha256,
388
389 Sha512,
391}
392
393impl HashAlg {
394 pub fn new(id: &str) -> Result<Self> {
401 Ok(id.parse()?)
402 }
403
404 pub fn as_str(self) -> &'static str {
406 match self {
407 HashAlg::Sha256 => SHA256,
408 HashAlg::Sha512 => SHA512,
409 }
410 }
411
412 pub const fn digest_size(self) -> usize {
414 match self {
415 HashAlg::Sha256 => 32,
416 HashAlg::Sha512 => 64,
417 }
418 }
419
420 #[cfg(feature = "alloc")]
422 pub fn digest(self, msg: &[u8]) -> Vec<u8> {
423 match self {
424 HashAlg::Sha256 => Sha256::digest(msg).to_vec(),
425 HashAlg::Sha512 => Sha512::digest(msg).to_vec(),
426 }
427 }
428}
429
430impl Label for HashAlg {}
431
432impl AsRef<str> for HashAlg {
433 fn as_ref(&self) -> &str {
434 self.as_str()
435 }
436}
437
438impl fmt::Display for HashAlg {
439 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440 f.write_str(self.as_str())
441 }
442}
443
444impl str::FromStr for HashAlg {
445 type Err = LabelError;
446
447 fn from_str(id: &str) -> core::result::Result<Self, LabelError> {
448 match id {
449 SHA256 => Ok(HashAlg::Sha256),
450 SHA512 => Ok(HashAlg::Sha512),
451 _ => Err(LabelError::new(id)),
452 }
453 }
454}
455
456#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
458#[non_exhaustive]
459pub enum KdfAlg {
460 None,
462
463 #[default]
465 Bcrypt,
466}
467
468impl KdfAlg {
469 pub fn new(kdfname: &str) -> Result<Self> {
474 Ok(kdfname.parse()?)
475 }
476
477 pub fn as_str(self) -> &'static str {
479 match self {
480 Self::None => NONE,
481 Self::Bcrypt => BCRYPT,
482 }
483 }
484
485 pub fn is_none(self) -> bool {
487 self == Self::None
488 }
489}
490
491impl Label for KdfAlg {}
492
493impl AsRef<str> for KdfAlg {
494 fn as_ref(&self) -> &str {
495 self.as_str()
496 }
497}
498
499impl fmt::Display for KdfAlg {
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 f.write_str(self.as_str())
502 }
503}
504
505impl str::FromStr for KdfAlg {
506 type Err = LabelError;
507
508 fn from_str(kdfname: &str) -> core::result::Result<Self, LabelError> {
509 match kdfname {
510 NONE => Ok(Self::None),
511 BCRYPT => Ok(Self::Bcrypt),
512 _ => Err(LabelError::new(kdfname)),
513 }
514 }
515}