1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3#![allow(clippy::module_name_repetitions)]
4
5use core::{fmt, str::FromStr};
6use std::error::Error;
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum CryptoParseError {
11 Empty,
12 Unknown,
13}
14
15impl fmt::Display for CryptoParseError {
16 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
17 match self {
18 Self::Empty => formatter.write_str("crypto label cannot be empty"),
19 Self::Unknown => formatter.write_str("unknown crypto label"),
20 }
21 }
22}
23
24impl Error for CryptoParseError {}
25
26macro_rules! label_enum {
27 ($name:ident { $($variant:ident => $label:literal),+ $(,)? }) => {
28 impl $name {
29 #[must_use]
31 pub const fn as_str(self) -> &'static str {
32 match self {
33 $(Self::$variant => $label,)+
34 }
35 }
36 }
37
38 impl fmt::Display for $name {
39 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40 formatter.write_str(self.as_str())
41 }
42 }
43
44 impl FromStr for $name {
45 type Err = CryptoParseError;
46
47 fn from_str(input: &str) -> Result<Self, Self::Err> {
48 let trimmed = input.trim();
49 if trimmed.is_empty() {
50 return Err(CryptoParseError::Empty);
51 }
52 let normalized = trimmed.to_ascii_lowercase();
53 match normalized.as_str() {
54 $($label => Ok(Self::$variant),)+
55 _ => Err(CryptoParseError::Unknown),
56 }
57 }
58 }
59 };
60}
61
62#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
64pub enum CryptoAlgorithm {
65 Hash,
66 Signature,
67 Encryption,
68 KeyAgreement,
69 KeyDerivation,
70 Unknown,
71}
72
73label_enum!(CryptoAlgorithm {
74 Hash => "hash",
75 Signature => "signature",
76 Encryption => "encryption",
77 KeyAgreement => "key-agreement",
78 KeyDerivation => "key-derivation",
79 Unknown => "unknown",
80});
81
82#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
84pub enum HashAlgorithm {
85 Sha256,
86 Sha384,
87 Sha512,
88 Blake2b,
89 Blake2s,
90 Md5,
91 Sha1,
92 Unknown,
93}
94
95label_enum!(HashAlgorithm {
96 Sha256 => "sha-256",
97 Sha384 => "sha-384",
98 Sha512 => "sha-512",
99 Blake2b => "blake2b",
100 Blake2s => "blake2s",
101 Md5 => "md5",
102 Sha1 => "sha-1",
103 Unknown => "unknown",
104});
105
106impl HashAlgorithm {
107 #[must_use]
109 pub const fn strength(self) -> CryptoStrength {
110 match self {
111 Self::Md5 | Self::Sha1 => CryptoStrength::Deprecated,
112 Self::Unknown => CryptoStrength::Unknown,
113 Self::Sha256 | Self::Sha384 | Self::Sha512 | Self::Blake2b | Self::Blake2s => {
114 CryptoStrength::Strong
115 }
116 }
117 }
118
119 #[must_use]
121 pub const fn is_deprecated_like(self) -> bool {
122 matches!(self, Self::Md5 | Self::Sha1)
123 }
124}
125
126#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
128pub enum SignatureAlgorithm {
129 Ed25519,
130 EcdsaP256,
131 EcdsaP384,
132 RsaPss,
133 RsaPkcs1v15,
134 Unknown,
135}
136
137label_enum!(SignatureAlgorithm {
138 Ed25519 => "ed25519",
139 EcdsaP256 => "ecdsa-p256",
140 EcdsaP384 => "ecdsa-p384",
141 RsaPss => "rsa-pss",
142 RsaPkcs1v15 => "rsa-pkcs1-v1-5",
143 Unknown => "unknown",
144});
145
146#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
148pub enum EncryptionAlgorithm {
149 AesGcm,
150 ChaCha20Poly1305,
151 XChaCha20Poly1305,
152 RsaOaep,
153 Unknown,
154}
155
156label_enum!(EncryptionAlgorithm {
157 AesGcm => "aes-gcm",
158 ChaCha20Poly1305 => "chacha20-poly1305",
159 XChaCha20Poly1305 => "xchacha20-poly1305",
160 RsaOaep => "rsa-oaep",
161 Unknown => "unknown",
162});
163
164#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
166pub enum KeyAlgorithm {
167 Ed25519,
168 EcdsaP256,
169 EcdsaP384,
170 Rsa,
171 Aes,
172 ChaCha20,
173 Unknown,
174}
175
176label_enum!(KeyAlgorithm {
177 Ed25519 => "ed25519",
178 EcdsaP256 => "ecdsa-p256",
179 EcdsaP384 => "ecdsa-p384",
180 Rsa => "rsa",
181 Aes => "aes",
182 ChaCha20 => "chacha20",
183 Unknown => "unknown",
184});
185
186#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
188pub enum KeyKind {
189 Public,
190 Private,
191 Symmetric,
192 Secret,
193 Certificate,
194}
195
196label_enum!(KeyKind {
197 Public => "public",
198 Private => "private",
199 Symmetric => "symmetric",
200 Secret => "secret",
201 Certificate => "certificate",
202});
203
204#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
206pub enum KeyUsage {
207 Sign,
208 Verify,
209 Encrypt,
210 Decrypt,
211 Wrap,
212 Unwrap,
213 Derive,
214 Authenticate,
215}
216
217label_enum!(KeyUsage {
218 Sign => "sign",
219 Verify => "verify",
220 Encrypt => "encrypt",
221 Decrypt => "decrypt",
222 Wrap => "wrap",
223 Unwrap => "unwrap",
224 Derive => "derive",
225 Authenticate => "authenticate",
226});
227
228#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
230pub enum CryptoEncoding {
231 Pem,
232 Der,
233 Jwk,
234 Base64,
235 Hex,
236 Raw,
237 Unknown,
238}
239
240label_enum!(CryptoEncoding {
241 Pem => "pem",
242 Der => "der",
243 Jwk => "jwk",
244 Base64 => "base64",
245 Hex => "hex",
246 Raw => "raw",
247 Unknown => "unknown",
248});
249
250#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
252pub enum CryptoStrength {
253 Deprecated,
254 Weak,
255 Acceptable,
256 Strong,
257 Unknown,
258}
259
260label_enum!(CryptoStrength {
261 Deprecated => "deprecated",
262 Weak => "weak",
263 Acceptable => "acceptable",
264 Strong => "strong",
265 Unknown => "unknown",
266});
267
268#[must_use]
270pub const fn is_deprecated_like(algorithm: HashAlgorithm) -> bool {
271 algorithm.is_deprecated_like()
272}
273
274#[cfg(test)]
275mod tests {
276 use super::{CryptoStrength, HashAlgorithm, SignatureAlgorithm, is_deprecated_like};
277
278 #[test]
279 fn marks_legacy_hash_labels() {
280 assert!(HashAlgorithm::Md5.is_deprecated_like());
281 assert!(HashAlgorithm::Sha1.is_deprecated_like());
282 assert!(is_deprecated_like(HashAlgorithm::Md5));
283 assert_eq!(HashAlgorithm::Sha256.strength(), CryptoStrength::Strong);
284 }
285
286 #[test]
287 fn parses_and_displays_labels() {
288 assert_eq!(
289 "ed25519".parse::<SignatureAlgorithm>().expect("signature"),
290 SignatureAlgorithm::Ed25519
291 );
292 assert_eq!(HashAlgorithm::Sha512.to_string(), "sha-512");
293 }
294}