licenz_core/crypto/
mod.rs1pub mod ed25519;
40pub mod rsa;
41
42#[cfg(feature = "post-quantum")]
44pub mod hybrid;
45#[cfg(feature = "post-quantum")]
46pub mod ml_dsa;
47#[cfg(feature = "post-quantum")]
48pub mod ml_kem;
49
50#[cfg(all(test, feature = "post-quantum"))]
52mod pq_tests;
53
54use crate::error::{LicenseError, Result};
55use std::collections::HashMap;
56use std::sync::LazyLock;
57
58pub mod algorithm_ids {
60 pub const RSA_SHA256: &str = "RSA-SHA256";
62 pub const ED25519: &str = "Ed25519";
64
65 #[cfg(feature = "post-quantum")]
69 pub const ML_DSA_65: &str = "ML-DSA-65";
70
71 #[cfg(feature = "post-quantum")]
74 pub const ML_KEM_768: &str = "ML-KEM-768";
75
76 #[cfg(feature = "post-quantum")]
78 pub const HYBRID_RSA_ML_DSA_65: &str = "Hybrid-RSA-ML-DSA-65";
79
80 #[cfg(feature = "post-quantum")]
82 pub const HYBRID_ED25519_ML_DSA_65: &str = "Hybrid-Ed25519-ML-DSA-65";
83}
84
85pub trait SignatureAlgorithm: Send + Sync {
90 fn algorithm_id(&self) -> &'static str;
92
93 fn sign(&self, data: &[u8], private_key_pem: &str) -> Result<Vec<u8>>;
95
96 fn verify(&self, data: &[u8], signature: &[u8], public_key_pem: &str) -> Result<()>;
98
99 fn generate_keypair(&self) -> Result<(String, String)>;
104
105 fn extract_public_key(&self, private_key_pem: &str) -> Result<String>;
107}
108
109pub trait EncryptionAlgorithm: Send + Sync {
111 fn algorithm_id(&self) -> &'static str;
113
114 fn encrypt(&self, data: &[u8], key: &[u8]) -> Result<Vec<u8>>;
116
117 fn decrypt(&self, data: &[u8], key: &[u8]) -> Result<Vec<u8>>;
119
120 fn key_size(&self) -> usize;
122
123 fn nonce_size(&self) -> usize;
125}
126
127pub type BoxedSignatureAlgorithm = Box<dyn SignatureAlgorithm>;
129
130pub type BoxedEncryptionAlgorithm = Box<dyn EncryptionAlgorithm>;
132
133pub struct CryptoRegistry;
138
139static SIGNATURE_ALGORITHMS: LazyLock<HashMap<&'static str, BoxedSignatureAlgorithm>> =
141 LazyLock::new(|| {
142 let mut map: HashMap<&'static str, BoxedSignatureAlgorithm> = HashMap::new();
143
144 map.insert(algorithm_ids::RSA_SHA256, Box::new(rsa::RsaSigner::new()));
146 map.insert(
147 algorithm_ids::ED25519,
148 Box::new(ed25519::Ed25519Signer::new()),
149 );
150
151 #[cfg(feature = "post-quantum")]
153 {
154 map.insert(
155 algorithm_ids::ML_DSA_65,
156 Box::new(ml_dsa::MlDsa65Signer::new()),
157 );
158 map.insert(
159 algorithm_ids::HYBRID_RSA_ML_DSA_65,
160 Box::new(hybrid::HybridRsaMlDsaSigner::new()),
161 );
162 map.insert(
163 algorithm_ids::HYBRID_ED25519_ML_DSA_65,
164 Box::new(hybrid::HybridEd25519MlDsaSigner::new()),
165 );
166 }
167
168 map
169 });
170
171impl CryptoRegistry {
172 pub fn get_signature_algorithm(algorithm_id: &str) -> Result<&'static dyn SignatureAlgorithm> {
174 SIGNATURE_ALGORITHMS
175 .get(algorithm_id)
176 .map(|boxed| boxed.as_ref())
177 .ok_or_else(|| {
178 LicenseError::InvalidKeyFormat(format!(
179 "Unknown signature algorithm: {}. Supported: {:?}",
180 algorithm_id,
181 Self::supported_signature_algorithms()
182 ))
183 })
184 }
185
186 pub fn supported_signature_algorithms() -> Vec<&'static str> {
188 SIGNATURE_ALGORITHMS.keys().copied().collect()
189 }
190
191 pub fn is_signature_algorithm_supported(algorithm_id: &str) -> bool {
193 SIGNATURE_ALGORITHMS.contains_key(algorithm_id)
194 }
195
196 pub fn default_signature_algorithm() -> &'static dyn SignatureAlgorithm {
198 SIGNATURE_ALGORITHMS
199 .get(algorithm_ids::RSA_SHA256)
200 .map(|boxed| boxed.as_ref())
201 .expect("RSA-SHA256 should always be available")
202 }
203
204 pub fn is_post_quantum_available() -> bool {
206 #[cfg(feature = "post-quantum")]
207 {
208 true
209 }
210 #[cfg(not(feature = "post-quantum"))]
211 {
212 false
213 }
214 }
215
216 #[cfg(feature = "post-quantum")]
218 pub fn post_quantum_signature_algorithms() -> Vec<&'static str> {
219 vec![
220 algorithm_ids::ML_DSA_65,
221 algorithm_ids::HYBRID_RSA_ML_DSA_65,
222 algorithm_ids::HYBRID_ED25519_ML_DSA_65,
223 ]
224 }
225
226 #[cfg(not(feature = "post-quantum"))]
228 pub fn post_quantum_signature_algorithms() -> Vec<&'static str> {
229 vec![]
230 }
231
232 pub fn classical_signature_algorithms() -> Vec<&'static str> {
234 vec![algorithm_ids::RSA_SHA256, algorithm_ids::ED25519]
235 }
236
237 #[cfg(feature = "post-quantum")]
239 pub fn hybrid_signature_algorithms() -> Vec<&'static str> {
240 vec![
241 algorithm_ids::HYBRID_RSA_ML_DSA_65,
242 algorithm_ids::HYBRID_ED25519_ML_DSA_65,
243 ]
244 }
245
246 #[cfg(not(feature = "post-quantum"))]
248 pub fn hybrid_signature_algorithms() -> Vec<&'static str> {
249 vec![]
250 }
251
252 pub fn recommended_algorithm() -> &'static dyn SignatureAlgorithm {
254 #[cfg(feature = "post-quantum")]
255 {
256 SIGNATURE_ALGORITHMS
257 .get(algorithm_ids::HYBRID_ED25519_ML_DSA_65)
258 .map(|boxed| boxed.as_ref())
259 .expect("Hybrid-Ed25519-ML-DSA-65 should be available with post-quantum feature")
260 }
261 #[cfg(not(feature = "post-quantum"))]
262 {
263 SIGNATURE_ALGORITHMS
264 .get(algorithm_ids::ED25519)
265 .map(|boxed| boxed.as_ref())
266 .expect("Ed25519 should always be available")
267 }
268 }
269}
270
271pub use crate::keys::CryptoKeyPair;
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_registry_get_rsa() {
280 let alg = CryptoRegistry::get_signature_algorithm(algorithm_ids::RSA_SHA256).unwrap();
281 assert_eq!(alg.algorithm_id(), algorithm_ids::RSA_SHA256);
282 }
283
284 #[test]
285 fn test_registry_get_ed25519() {
286 let alg = CryptoRegistry::get_signature_algorithm(algorithm_ids::ED25519).unwrap();
287 assert_eq!(alg.algorithm_id(), algorithm_ids::ED25519);
288 }
289
290 #[test]
291 fn test_registry_unknown_algorithm() {
292 let result = CryptoRegistry::get_signature_algorithm("UNKNOWN-ALG");
293 assert!(result.is_err());
294 }
295
296 #[test]
297 fn test_supported_algorithms() {
298 let supported = CryptoRegistry::supported_signature_algorithms();
299 assert!(supported.contains(&algorithm_ids::RSA_SHA256));
300 assert!(supported.contains(&algorithm_ids::ED25519));
301 }
302
303 #[test]
304 fn test_default_algorithm() {
305 let alg = CryptoRegistry::default_signature_algorithm();
306 assert_eq!(alg.algorithm_id(), algorithm_ids::RSA_SHA256);
307 }
308
309 #[test]
310 fn test_crypto_keypair_rsa() {
311 let keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
312 assert_eq!(keypair.algorithm_id, algorithm_ids::RSA_SHA256);
313 assert!(keypair.private_key_pem().contains("PRIVATE KEY"));
314 assert!(keypair.public_key_pem.contains("PUBLIC KEY"));
315
316 let data = b"test message";
317 let signature = keypair.sign(data).unwrap();
318 assert!(keypair.verify(data, &signature).is_ok());
319 }
320
321 #[test]
322 fn test_crypto_keypair_ed25519() {
323 let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
324 assert_eq!(keypair.algorithm_id, algorithm_ids::ED25519);
325 assert!(keypair.private_key_pem().contains("PRIVATE KEY"));
326 assert!(keypair.public_key_pem.contains("PUBLIC KEY"));
327
328 let data = b"test message";
329 let signature = keypair.sign(data).unwrap();
330 assert!(keypair.verify(data, &signature).is_ok());
331 }
332}