1use crate::crypto::CryptoRegistry;
21use crate::error::{LicenseError, Result};
22use crate::keys::parse_private_key;
23use crate::license::{LicenseData, SignedLicense, BINARY_MAGIC, BINARY_VERSION};
24use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
25use rsa::pkcs1v15::SigningKey;
26use rsa::signature::{RandomizedSigner, SignatureEncoding};
27use rsa::RsaPrivateKey;
28use sha2::Sha256;
29use std::io::Write;
30use std::path::Path;
31use zeroize::Zeroizing;
32
33pub struct LicenseGenerator {
35 private_key: RsaPrivateKey,
36}
37
38impl LicenseGenerator {
39 pub fn new(private_key: RsaPrivateKey) -> Self {
41 Self { private_key }
42 }
43
44 pub fn from_pem(pem: &str) -> Result<Self> {
46 let private_key = parse_private_key(pem)?;
47 Ok(Self::new(private_key))
48 }
49
50 pub fn from_pem_file(path: &Path) -> Result<Self> {
52 let pem = std::fs::read_to_string(path)?;
53 Self::from_pem(&pem)
54 }
55
56 pub fn generate(&self, data: LicenseData) -> Result<SignedLicense> {
58 let data_bytes = serde_json::to_vec(&data)
60 .map_err(|e| LicenseError::SerializationError(e.to_string()))?;
61
62 let signature = self.sign(&data_bytes)?;
64
65 Ok(SignedLicense {
66 data,
67 signature: BASE64.encode(&signature),
68 algorithm: "RSA-SHA256".to_string(),
69 })
70 }
71
72 fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
74 let signing_key = SigningKey::<Sha256>::new(self.private_key.clone());
75 let mut rng = rand::rngs::OsRng;
76
77 let signature = signing_key.sign_with_rng(&mut rng, data);
78
79 Ok(signature.to_bytes().to_vec())
80 }
81
82 pub fn export_binary(&self, license: &SignedLicense) -> Result<Vec<u8>> {
84 let mut output = Vec::new();
85
86 output.write_all(BINARY_MAGIC)?;
88
89 output.write_all(&[BINARY_VERSION])?;
91
92 let encoded = serde_json::to_vec(license)
94 .map_err(|e| LicenseError::SerializationError(e.to_string()))?;
95
96 let len = encoded.len() as u32;
98 output.write_all(&len.to_le_bytes())?;
99
100 output.write_all(&encoded)?;
102
103 Ok(output)
104 }
105
106 pub fn export_json(&self, license: &SignedLicense) -> Result<String> {
108 serde_json::to_string_pretty(license)
109 .map_err(|e| LicenseError::SerializationError(e.to_string()))
110 }
111
112 pub fn save_binary(&self, license: &SignedLicense, path: &Path) -> Result<()> {
114 let binary = self.export_binary(license)?;
115 std::fs::write(path, binary)?;
116 Ok(())
117 }
118
119 pub fn save_json(&self, license: &SignedLicense, path: &Path) -> Result<()> {
121 let json = self.export_json(license)?;
122 std::fs::write(path, json)?;
123 Ok(())
124 }
125}
126
127pub struct CryptoGenerator {
150 private_key_pem: Zeroizing<String>,
152 algorithm_id: String,
154}
155
156impl std::fmt::Debug for CryptoGenerator {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 f.debug_struct("CryptoGenerator")
159 .field("private_key_pem", &"[REDACTED]")
160 .field("algorithm_id", &self.algorithm_id)
161 .finish()
162 }
163}
164
165impl CryptoGenerator {
166 pub fn new(private_key_pem: &str, algorithm_id: &str) -> Self {
172 Self {
173 private_key_pem: Zeroizing::new(private_key_pem.to_string()),
174 algorithm_id: algorithm_id.to_string(),
175 }
176 }
177
178 pub fn from_pem_file(path: &Path, algorithm_id: &str) -> Result<Self> {
180 let pem = std::fs::read_to_string(path)?;
181 Ok(Self::new(&pem, algorithm_id))
182 }
183
184 pub fn from_keypair(keypair: &crate::keys::CryptoKeyPair) -> Self {
186 Self::new(keypair.private_key_pem(), &keypair.algorithm_id)
187 }
188
189 pub fn algorithm_id(&self) -> &str {
191 &self.algorithm_id
192 }
193
194 pub fn generate(&self, data: LicenseData) -> Result<SignedLicense> {
196 let data_bytes = serde_json::to_vec(&data)
198 .map_err(|e| LicenseError::SerializationError(e.to_string()))?;
199
200 let algorithm = CryptoRegistry::get_signature_algorithm(&self.algorithm_id)?;
202 let signature = algorithm.sign(&data_bytes, &self.private_key_pem)?;
203
204 Ok(SignedLicense {
205 data,
206 signature: BASE64.encode(&signature),
207 algorithm: self.algorithm_id.clone(),
208 })
209 }
210
211 pub fn export_binary(&self, license: &SignedLicense) -> Result<Vec<u8>> {
213 let mut output = Vec::new();
214
215 output.write_all(BINARY_MAGIC)?;
217
218 output.write_all(&[BINARY_VERSION])?;
220
221 let encoded = serde_json::to_vec(license)
223 .map_err(|e| LicenseError::SerializationError(e.to_string()))?;
224
225 let len = encoded.len() as u32;
227 output.write_all(&len.to_le_bytes())?;
228
229 output.write_all(&encoded)?;
231
232 Ok(output)
233 }
234
235 pub fn export_json(&self, license: &SignedLicense) -> Result<String> {
237 serde_json::to_string_pretty(license)
238 .map_err(|e| LicenseError::SerializationError(e.to_string()))
239 }
240
241 pub fn save_binary(&self, license: &SignedLicense, path: &Path) -> Result<()> {
243 let binary = self.export_binary(license)?;
244 std::fs::write(path, binary)?;
245 Ok(())
246 }
247
248 pub fn save_json(&self, license: &SignedLicense, path: &Path) -> Result<()> {
250 let json = self.export_json(license)?;
251 std::fs::write(path, json)?;
252 Ok(())
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use crate::algorithm_ids;
260 use crate::keys::KeyPair;
261 use crate::keys::KeySize;
262
263 #[test]
264 fn test_license_generation() {
265 let keypair = KeyPair::generate(KeySize::Bits2048).unwrap();
266 let generator = LicenseGenerator::new(keypair.into_private_key());
267
268 let data = LicenseData::builder()
269 .id("TEST-001")
270 .serial("SN-12345")
271 .customer_id("CUST-001")
272 .product_id("PROD-001")
273 .valid_days(365)
274 .feature("basic")
275 .build()
276 .unwrap();
277
278 let signed = generator.generate(data).unwrap();
279
280 assert!(!signed.signature.is_empty());
281 assert_eq!(signed.algorithm, "RSA-SHA256");
282 }
283
284 #[test]
285 fn test_binary_export() {
286 let keypair = KeyPair::generate(KeySize::Bits2048).unwrap();
287 let generator = LicenseGenerator::new(keypair.into_private_key());
288
289 let data = LicenseData::builder()
290 .id("TEST-001")
291 .serial("SN-12345")
292 .customer_id("CUST-001")
293 .product_id("PROD-001")
294 .valid_days(365)
295 .build()
296 .unwrap();
297
298 let signed = generator.generate(data).unwrap();
299 let binary = generator.export_binary(&signed).unwrap();
300
301 assert_eq!(&binary[0..4], BINARY_MAGIC);
303 assert_eq!(binary[4], BINARY_VERSION);
304 }
305
306 #[test]
307 fn test_crypto_generator_rsa() {
308 use crate::keys::CryptoKeyPair;
309
310 let keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
311 let generator = CryptoGenerator::from_keypair(&keypair);
312
313 let data = LicenseData::builder()
314 .id("CRYPTO-RSA-001")
315 .serial("SN-CRYPTO-RSA")
316 .customer_id("CUST-001")
317 .product_id("PROD-001")
318 .valid_days(365)
319 .feature("basic")
320 .build()
321 .unwrap();
322
323 let signed = generator.generate(data).unwrap();
324
325 assert!(!signed.signature.is_empty());
326 assert_eq!(signed.algorithm, algorithm_ids::RSA_SHA256);
327 }
328
329 #[test]
330 fn test_crypto_generator_ed25519() {
331 use crate::keys::CryptoKeyPair;
332
333 let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
334 let generator = CryptoGenerator::from_keypair(&keypair);
335
336 let data = LicenseData::builder()
337 .id("CRYPTO-ED25519-001")
338 .serial("SN-CRYPTO-ED25519")
339 .customer_id("CUST-001")
340 .product_id("PROD-001")
341 .valid_days(365)
342 .feature("basic")
343 .build()
344 .unwrap();
345
346 let signed = generator.generate(data).unwrap();
347
348 assert!(!signed.signature.is_empty());
349 assert_eq!(signed.algorithm, algorithm_ids::ED25519);
350
351 let algorithm = CryptoRegistry::get_signature_algorithm(algorithm_ids::ED25519).unwrap();
353 let data_bytes = serde_json::to_vec(&signed.data).unwrap();
354 let sig_bytes = BASE64.decode(&signed.signature).unwrap();
355 assert!(algorithm
356 .verify(&data_bytes, &sig_bytes, &keypair.public_key_pem)
357 .is_ok());
358 }
359
360 #[test]
361 fn test_crypto_generator_binary_export() {
362 use crate::keys::CryptoKeyPair;
363
364 let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
365 let generator = CryptoGenerator::from_keypair(&keypair);
366
367 let data = LicenseData::builder()
368 .id("BINARY-ED25519-001")
369 .serial("SN-BINARY-ED25519")
370 .customer_id("CUST-001")
371 .product_id("PROD-001")
372 .valid_days(365)
373 .build()
374 .unwrap();
375
376 let signed = generator.generate(data).unwrap();
377 let binary = generator.export_binary(&signed).unwrap();
378
379 assert_eq!(&binary[0..4], BINARY_MAGIC);
381 assert_eq!(binary[4], BINARY_VERSION);
382 }
383
384 #[cfg(feature = "post-quantum")]
386 mod pq_tests {
387 use super::*;
388 use crate::crypto::algorithm_ids;
389 use crate::keys::CryptoKeyPair;
390
391 #[test]
392 fn test_crypto_generator_ml_dsa_65() {
393 let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
394 let generator = CryptoGenerator::from_keypair(&keypair);
395
396 let data = LicenseData::builder()
397 .id("PQ-ML-DSA-001")
398 .serial("SN-PQ-ML-DSA")
399 .customer_id("CUST-001")
400 .product_id("PROD-001")
401 .valid_days(365)
402 .feature("quantum-safe")
403 .build()
404 .unwrap();
405
406 let signed = generator.generate(data).unwrap();
407
408 assert!(!signed.signature.is_empty());
409 assert_eq!(signed.algorithm, algorithm_ids::ML_DSA_65);
410
411 assert!(signed.signature.len() > 4000);
413 }
414
415 #[test]
416 fn test_crypto_generator_hybrid_ed25519_ml_dsa() {
417 let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_ED25519_ML_DSA_65).unwrap();
418 let generator = CryptoGenerator::from_keypair(&keypair);
419
420 let data = LicenseData::builder()
421 .id("PQ-HYBRID-001")
422 .serial("SN-PQ-HYBRID")
423 .customer_id("CUST-001")
424 .product_id("PROD-001")
425 .valid_days(365)
426 .feature("hybrid-security")
427 .build()
428 .unwrap();
429
430 let signed = generator.generate(data).unwrap();
431
432 assert!(!signed.signature.is_empty());
433 assert_eq!(signed.algorithm, algorithm_ids::HYBRID_ED25519_ML_DSA_65);
434 }
435
436 #[test]
437 fn test_crypto_generator_hybrid_rsa_ml_dsa() {
438 let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_RSA_ML_DSA_65).unwrap();
439 let generator = CryptoGenerator::from_keypair(&keypair);
440
441 let data = LicenseData::builder()
442 .id("PQ-HYBRID-RSA-001")
443 .serial("SN-PQ-HYBRID-RSA")
444 .customer_id("CUST-001")
445 .product_id("PROD-001")
446 .valid_days(365)
447 .feature("hybrid-rsa-security")
448 .build()
449 .unwrap();
450
451 let signed = generator.generate(data).unwrap();
452
453 assert!(!signed.signature.is_empty());
454 assert_eq!(signed.algorithm, algorithm_ids::HYBRID_RSA_ML_DSA_65);
455 }
456
457 #[test]
458 fn test_pq_license_binary_export() {
459 let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
460 let generator = CryptoGenerator::from_keypair(&keypair);
461
462 let data = LicenseData::builder()
463 .id("PQ-BINARY-001")
464 .serial("SN-PQ-BINARY")
465 .customer_id("CUST-001")
466 .product_id("PROD-001")
467 .valid_days(365)
468 .build()
469 .unwrap();
470
471 let signed = generator.generate(data).unwrap();
472 let binary = generator.export_binary(&signed).unwrap();
473
474 assert_eq!(&binary[0..4], BINARY_MAGIC);
476 assert_eq!(binary[4], BINARY_VERSION);
477
478 assert!(
483 binary.len() > 4500,
484 "Expected binary > 4500 bytes, got {} bytes. Signature len: {}",
485 binary.len(),
486 signed.signature.len()
487 );
488 }
489 }
490}