Skip to main content

use_crypto/
lib.rs

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/// Error returned when a crypto label cannot be parsed.
9#[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            /// Returns the stable label.
30            #[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/// Broad cryptographic algorithm category labels.
63#[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/// Hash algorithm labels.
83#[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    /// Returns strength metadata for this hash label.
108    #[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    /// Returns `true` for deprecated or weak hash labels.
120    #[must_use]
121    pub const fn is_deprecated_like(self) -> bool {
122        matches!(self, Self::Md5 | Self::Sha1)
123    }
124}
125
126/// Signature algorithm labels.
127#[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/// Encryption algorithm labels.
147#[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/// Key algorithm labels.
165#[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/// Key kind labels.
187#[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/// Key usage labels.
205#[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/// Cryptographic encoding labels.
229#[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/// Cryptographic strength labels.
251#[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/// Returns `true` when a hash algorithm label is deprecated-like.
269#[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}