1use crate::crypto::{algorithm_ids, CryptoRegistry, SignatureAlgorithm};
22use crate::error::{LicenseError, Result};
23use pem::{encode, Pem};
24use rand::rngs::OsRng;
25use rsa::pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey};
26use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
27use rsa::{RsaPrivateKey, RsaPublicKey};
28use std::path::Path;
29use zeroize::Zeroizing;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
33pub enum KeySize {
34 Bits2048,
35 #[default]
36 Bits3072,
37 Bits4096,
38}
39
40impl KeySize {
41 pub fn bits(&self) -> usize {
42 match self {
43 KeySize::Bits2048 => 2048,
44 KeySize::Bits3072 => 3072,
45 KeySize::Bits4096 => 4096,
46 }
47 }
48}
49
50pub struct KeyPair {
52 private_key: RsaPrivateKey,
53 pub public_key: RsaPublicKey,
54}
55
56impl std::fmt::Debug for KeyPair {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("KeyPair")
59 .field("private_key", &"[REDACTED]")
60 .field("public_key", &"<RsaPublicKey>")
61 .finish()
62 }
63}
64
65impl KeyPair {
66 pub fn private_key(&self) -> &RsaPrivateKey {
68 &self.private_key
69 }
70
71 pub fn into_private_key(self) -> RsaPrivateKey {
73 self.private_key
74 }
75
76 pub fn generate(size: KeySize) -> Result<Self> {
78 let mut rng = OsRng;
79 let private_key = RsaPrivateKey::new(&mut rng, size.bits())
80 .map_err(|e| LicenseError::KeyGenerationFailed(e.to_string()))?;
81 let public_key = RsaPublicKey::from(&private_key);
82
83 Ok(Self {
84 private_key,
85 public_key,
86 })
87 }
88
89 pub fn export_private_pem(&self) -> Result<String> {
91 let der = self
92 .private_key
93 .to_pkcs8_der()
94 .map_err(|e| LicenseError::InvalidKeyFormat(e.to_string()))?;
95
96 let pem = Pem::new("PRIVATE KEY", der.as_bytes());
97 Ok(encode(&pem))
98 }
99
100 pub fn export_public_pem(&self) -> Result<String> {
102 let der = self
103 .public_key
104 .to_public_key_der()
105 .map_err(|e| LicenseError::InvalidKeyFormat(e.to_string()))?;
106
107 let pem = Pem::new("PUBLIC KEY", der.as_bytes());
108 Ok(encode(&pem))
109 }
110
111 pub fn save_to_files(&self, private_path: &Path, public_path: &Path) -> Result<()> {
113 std::fs::write(private_path, self.export_private_pem()?)?;
114 std::fs::write(public_path, self.export_public_pem()?)?;
115 Ok(())
116 }
117
118 pub fn load_from_files(private_path: &Path, public_path: &Path) -> Result<Self> {
122 check_private_key_permissions(private_path)?;
123
124 let private_pem = std::fs::read_to_string(private_path)?;
125 let public_pem = std::fs::read_to_string(public_path)?;
126
127 let private_key = parse_private_key(&private_pem)?;
128 let public_key = parse_public_key(&public_pem)?;
129
130 Ok(Self {
131 private_key,
132 public_key,
133 })
134 }
135}
136
137pub fn parse_private_key(pem_str: &str) -> Result<RsaPrivateKey> {
139 let pem_str = pem_str.replace("\\n", "\n");
141
142 if let Ok(key) = RsaPrivateKey::from_pkcs8_pem(&pem_str) {
144 return Ok(key);
145 }
146
147 if let Ok(key) = RsaPrivateKey::from_pkcs1_pem(&pem_str) {
149 return Ok(key);
150 }
151
152 Err(LicenseError::InvalidKeyFormat(
153 "Could not parse private key (tried PKCS#8 and PKCS#1 formats)".into(),
154 ))
155}
156
157pub fn parse_public_key(pem_str: &str) -> Result<RsaPublicKey> {
159 let pem_str = pem_str.replace("\\n", "\n");
161
162 if let Ok(key) = RsaPublicKey::from_public_key_pem(&pem_str) {
164 return Ok(key);
165 }
166
167 if let Ok(key) = RsaPublicKey::from_pkcs1_pem(&pem_str) {
169 return Ok(key);
170 }
171
172 Err(LicenseError::InvalidKeyFormat(
173 "Could not parse public key (tried SPKI and PKCS#1 formats)".into(),
174 ))
175}
176
177pub fn extract_public_key(private_key: &RsaPrivateKey) -> RsaPublicKey {
179 RsaPublicKey::from(private_key)
180}
181
182pub struct CryptoKeyPair {
190 private_key_pem: Zeroizing<String>,
192 pub public_key_pem: String,
194 pub algorithm_id: String,
196}
197
198impl std::fmt::Debug for CryptoKeyPair {
199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200 f.debug_struct("CryptoKeyPair")
201 .field("private_key_pem", &"[REDACTED]")
202 .field(
203 "public_key_pem",
204 &format!(
205 "{}...",
206 &self.public_key_pem[..40.min(self.public_key_pem.len())]
207 ),
208 )
209 .field("algorithm_id", &self.algorithm_id)
210 .finish()
211 }
212}
213
214impl CryptoKeyPair {
215 pub fn private_key_pem(&self) -> &str {
217 &self.private_key_pem
218 }
219
220 pub fn generate(algorithm_id: &str) -> Result<Self> {
233 let algorithm = CryptoRegistry::get_signature_algorithm(algorithm_id)?;
234 let (private_key_pem, public_key_pem) = algorithm.generate_keypair()?;
235 Ok(Self {
236 private_key_pem: Zeroizing::new(private_key_pem),
237 public_key_pem,
238 algorithm_id: algorithm_id.to_string(),
239 })
240 }
241
242 pub fn from_pem(private_key_pem: String, public_key_pem: String, algorithm_id: &str) -> Self {
249 Self {
250 private_key_pem: Zeroizing::new(private_key_pem),
251 public_key_pem,
252 algorithm_id: algorithm_id.to_string(),
253 }
254 }
255
256 pub fn load_from_files(
265 private_path: &Path,
266 public_path: &Path,
267 algorithm_id: &str,
268 ) -> Result<Self> {
269 check_private_key_permissions(private_path)?;
270
271 let private_key_pem = std::fs::read_to_string(private_path)?;
272 let public_key_pem = std::fs::read_to_string(public_path)?;
273 Ok(Self::from_pem(
274 private_key_pem,
275 public_key_pem,
276 algorithm_id,
277 ))
278 }
279
280 pub fn save_to_files(&self, private_path: &Path, public_path: &Path) -> Result<()> {
282 std::fs::write(private_path, self.private_key_pem.as_str())?;
283 std::fs::write(public_path, &self.public_key_pem)?;
284
285 #[cfg(unix)]
287 {
288 use std::os::unix::fs::PermissionsExt;
289 let perms = std::fs::Permissions::from_mode(0o600);
290 std::fs::set_permissions(private_path, perms)?;
291 }
292
293 Ok(())
294 }
295
296 pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
298 let algorithm = CryptoRegistry::get_signature_algorithm(&self.algorithm_id)?;
299 algorithm.sign(data, &self.private_key_pem)
300 }
301
302 pub fn verify(&self, data: &[u8], signature: &[u8]) -> Result<()> {
304 let algorithm = CryptoRegistry::get_signature_algorithm(&self.algorithm_id)?;
305 algorithm.verify(data, signature, &self.public_key_pem)
306 }
307
308 pub fn get_algorithm(&self) -> Result<&'static dyn SignatureAlgorithm> {
310 CryptoRegistry::get_signature_algorithm(&self.algorithm_id)
311 }
312
313 pub fn from_rsa_keypair(keypair: &KeyPair) -> Result<Self> {
315 Ok(Self {
316 private_key_pem: Zeroizing::new(keypair.export_private_pem()?),
317 public_key_pem: keypair.export_public_pem()?,
318 algorithm_id: algorithm_ids::RSA_SHA256.to_string(),
319 })
320 }
321}
322
323fn check_private_key_permissions(path: &Path) -> Result<()> {
328 #[cfg(unix)]
329 {
330 use std::os::unix::fs::PermissionsExt;
331
332 if !path.exists() {
333 return Ok(()); }
335
336 let metadata = std::fs::metadata(path)?;
337 let mode = metadata.permissions().mode();
338
339 if mode & 0o077 != 0 {
341 return Err(LicenseError::InsecureKeyPermissions {
342 path: path.to_path_buf(),
343 mode: format!("{:04o}", mode & 0o7777),
344 suggestion: "Run: chmod 600 <file>".to_string(),
345 });
346 }
347 }
348 #[cfg(not(unix))]
349 {
350 let _ = path; }
352 Ok(())
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358
359 #[test]
360 fn test_key_generation() {
361 let keypair = KeyPair::generate(KeySize::Bits2048).unwrap();
362
363 let private_pem = keypair.export_private_pem().unwrap();
364 let public_pem = keypair.export_public_pem().unwrap();
365
366 assert!(private_pem.contains("PRIVATE KEY"));
367 assert!(public_pem.contains("PUBLIC KEY"));
368 }
369
370 #[test]
371 fn test_key_round_trip() {
372 let keypair = KeyPair::generate(KeySize::Bits2048).unwrap();
373
374 let private_pem = keypair.export_private_pem().unwrap();
375 let public_pem = keypair.export_public_pem().unwrap();
376
377 let parsed_private = parse_private_key(&private_pem).unwrap();
378 let parsed_public = parse_public_key(&public_pem).unwrap();
379
380 assert_eq!(*keypair.private_key(), parsed_private);
381 assert_eq!(keypair.public_key, parsed_public);
382 }
383
384 #[test]
385 fn test_crypto_keypair_rsa() {
386 let keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
387 assert_eq!(keypair.algorithm_id, algorithm_ids::RSA_SHA256);
388 assert!(keypair.private_key_pem().contains("PRIVATE KEY"));
389 assert!(keypair.public_key_pem.contains("PUBLIC KEY"));
390
391 let data = b"test message for RSA";
393 let signature = keypair.sign(data).unwrap();
394 assert!(keypair.verify(data, &signature).is_ok());
395 }
396
397 #[test]
398 fn test_crypto_keypair_ed25519() {
399 let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
400 assert_eq!(keypair.algorithm_id, algorithm_ids::ED25519);
401 assert!(keypair.private_key_pem().contains("PRIVATE KEY"));
402 assert!(keypair.public_key_pem.contains("PUBLIC KEY"));
403
404 let data = b"test message for Ed25519";
406 let signature = keypair.sign(data).unwrap();
407 assert!(keypair.verify(data, &signature).is_ok());
408
409 assert_eq!(signature.len(), 64);
411 }
412
413 #[test]
414 fn test_crypto_keypair_from_rsa_keypair() {
415 let rsa_keypair = KeyPair::generate(KeySize::Bits2048).unwrap();
416 let crypto_keypair = CryptoKeyPair::from_rsa_keypair(&rsa_keypair).unwrap();
417
418 assert_eq!(crypto_keypair.algorithm_id, algorithm_ids::RSA_SHA256);
419
420 let data = b"conversion test";
422 let signature = crypto_keypair.sign(data).unwrap();
423 assert!(crypto_keypair.verify(data, &signature).is_ok());
424 }
425}