rust_license_key/
crypto.rs

1//! Cryptographic operations for license signing and verification.
2//!
3//! This module provides Ed25519 key pair generation, signing, and verification
4//! functionality. It uses the `ed25519-dalek` crate for cryptographic operations.
5//!
6//! # Security Notes
7//!
8//! - Private keys must be kept secret by the license publisher.
9//! - Public keys can be safely embedded in client applications.
10//! - The Ed25519 algorithm provides 128-bit security level.
11
12use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
13use base64::Engine;
14use ed25519_dalek::{
15    Signature, Signer, SigningKey, Verifier, VerifyingKey, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH,
16    SIGNATURE_LENGTH,
17};
18use rand::rngs::OsRng;
19
20use crate::error::{LicenseError, Result};
21
22// =============================================================================
23// Key Pair
24// =============================================================================
25
26/// An Ed25519 key pair for signing and verifying licenses.
27///
28/// The key pair consists of:
29/// - A private key (32 bytes) used by the publisher to sign licenses.
30/// - A public key (32 bytes) embedded in client applications for verification.
31///
32/// # Example
33///
34/// ```
35/// use rust_license_key::crypto::KeyPair;
36///
37/// // Generate a new key pair
38/// let key_pair = KeyPair::generate().expect("Failed to generate key pair");
39///
40/// // Export keys for storage
41/// let private_key_base64 = key_pair.private_key_base64();
42/// let public_key_base64 = key_pair.public_key_base64();
43///
44/// println!("Keep this private: {}", private_key_base64);
45/// println!("Embed in client: {}", public_key_base64);
46/// ```
47#[derive(Debug)]
48pub struct KeyPair {
49    /// The Ed25519 signing key (private key).
50    signing_key: SigningKey,
51}
52
53impl KeyPair {
54    /// Generates a new random Ed25519 key pair.
55    ///
56    /// Uses the operating system's cryptographically secure random number
57    /// generator (CSPRNG) to generate the key material.
58    ///
59    /// # Returns
60    ///
61    /// A new `KeyPair` instance, or an error if key generation fails.
62    ///
63    /// # Security
64    ///
65    /// The generated private key should be stored securely and never shared.
66    /// Only the public key should be distributed with client applications.
67    pub fn generate() -> Result<Self> {
68        // Use OS-provided CSPRNG for secure key generation
69        let mut csprng = OsRng;
70        let signing_key = SigningKey::generate(&mut csprng);
71
72        Ok(Self { signing_key })
73    }
74
75    /// Creates a key pair from a base64-encoded private key.
76    ///
77    /// # Arguments
78    ///
79    /// * `private_key_base64` - The base64-encoded private key (32 bytes when decoded).
80    ///
81    /// # Security
82    ///
83    /// This function should only be used in secure publisher-side tooling,
84    /// never in client applications.
85    pub fn from_private_key_base64(private_key_base64: &str) -> Result<Self> {
86        let private_key_bytes = BASE64_STANDARD.decode(private_key_base64).map_err(|e| {
87            LicenseError::InvalidPrivateKey {
88                reason: format!("invalid base64 encoding: {}", e),
89            }
90        })?;
91
92        if private_key_bytes.len() != SECRET_KEY_LENGTH {
93            return Err(LicenseError::InvalidPrivateKey {
94                reason: format!(
95                    "invalid key length: expected {} bytes, got {}",
96                    SECRET_KEY_LENGTH,
97                    private_key_bytes.len()
98                ),
99            });
100        }
101
102        let key_bytes: [u8; SECRET_KEY_LENGTH] =
103            private_key_bytes
104                .try_into()
105                .map_err(|_| LicenseError::InvalidPrivateKey {
106                    reason: "failed to convert key bytes".to_string(),
107                })?;
108
109        let signing_key = SigningKey::from_bytes(&key_bytes);
110
111        Ok(Self { signing_key })
112    }
113
114    /// Creates a key pair from raw private key bytes.
115    ///
116    /// # Arguments
117    ///
118    /// * `private_key_bytes` - The raw private key bytes (exactly 32 bytes).
119    pub fn from_private_key_bytes(private_key_bytes: &[u8]) -> Result<Self> {
120        if private_key_bytes.len() != SECRET_KEY_LENGTH {
121            return Err(LicenseError::InvalidPrivateKey {
122                reason: format!(
123                    "invalid key length: expected {} bytes, got {}",
124                    SECRET_KEY_LENGTH,
125                    private_key_bytes.len()
126                ),
127            });
128        }
129
130        let key_bytes: [u8; SECRET_KEY_LENGTH] =
131            private_key_bytes
132                .try_into()
133                .map_err(|_| LicenseError::InvalidPrivateKey {
134                    reason: "failed to convert key bytes".to_string(),
135                })?;
136
137        let signing_key = SigningKey::from_bytes(&key_bytes);
138
139        Ok(Self { signing_key })
140    }
141
142    /// Returns the public key for this key pair.
143    pub fn public_key(&self) -> PublicKey {
144        PublicKey {
145            verifying_key: self.signing_key.verifying_key(),
146        }
147    }
148
149    /// Returns the private key as raw bytes.
150    ///
151    /// # Security
152    ///
153    /// Handle the returned bytes with care. They should be stored securely
154    /// and never exposed to untrusted parties.
155    pub fn private_key_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
156        self.signing_key.to_bytes()
157    }
158
159    /// Returns the private key as a base64-encoded string.
160    ///
161    /// # Security
162    ///
163    /// Handle the returned string with care. It should be stored securely
164    /// and never exposed to untrusted parties.
165    pub fn private_key_base64(&self) -> String {
166        BASE64_STANDARD.encode(self.signing_key.to_bytes())
167    }
168
169    /// Returns the public key as a base64-encoded string.
170    ///
171    /// This is the value that should be embedded in client applications.
172    pub fn public_key_base64(&self) -> String {
173        self.public_key().to_base64()
174    }
175
176    /// Signs the given data with the private key.
177    ///
178    /// # Arguments
179    ///
180    /// * `data` - The data to sign (typically the serialized license payload).
181    ///
182    /// # Returns
183    ///
184    /// The Ed25519 signature as raw bytes.
185    pub fn sign(&self, data: &[u8]) -> [u8; SIGNATURE_LENGTH] {
186        let signature = self.signing_key.sign(data);
187        signature.to_bytes()
188    }
189
190    /// Signs the given data and returns the signature as base64.
191    ///
192    /// # Arguments
193    ///
194    /// * `data` - The data to sign.
195    ///
196    /// # Returns
197    ///
198    /// The base64-encoded signature.
199    pub fn sign_base64(&self, data: &[u8]) -> String {
200        BASE64_STANDARD.encode(self.sign(data))
201    }
202}
203
204// =============================================================================
205// Public Key
206// =============================================================================
207
208/// An Ed25519 public key for verifying license signatures.
209///
210/// This is the key that should be embedded in client applications.
211/// It can only verify signatures, not create them.
212///
213/// # Example
214///
215/// ```
216/// use rust_license_key::crypto::PublicKey;
217///
218/// // Load from base64 (e.g., embedded in application)
219/// let public_key_base64 = "..."; // Your public key here
220/// // let public_key = PublicKey::from_base64(public_key_base64).unwrap();
221/// ```
222#[derive(Debug, Clone)]
223pub struct PublicKey {
224    /// The Ed25519 verifying key.
225    verifying_key: VerifyingKey,
226}
227
228impl PublicKey {
229    /// Creates a public key from a base64-encoded string.
230    ///
231    /// # Arguments
232    ///
233    /// * `public_key_base64` - The base64-encoded public key (32 bytes when decoded).
234    pub fn from_base64(public_key_base64: &str) -> Result<Self> {
235        let public_key_bytes = BASE64_STANDARD.decode(public_key_base64).map_err(|e| {
236            LicenseError::InvalidPublicKey {
237                reason: format!("invalid base64 encoding: {}", e),
238            }
239        })?;
240
241        Self::from_bytes(&public_key_bytes)
242    }
243
244    /// Creates a public key from raw bytes.
245    ///
246    /// # Arguments
247    ///
248    /// * `bytes` - The raw public key bytes (exactly 32 bytes).
249    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
250        if bytes.len() != PUBLIC_KEY_LENGTH {
251            return Err(LicenseError::InvalidPublicKey {
252                reason: format!(
253                    "invalid key length: expected {} bytes, got {}",
254                    PUBLIC_KEY_LENGTH,
255                    bytes.len()
256                ),
257            });
258        }
259
260        let key_bytes: [u8; PUBLIC_KEY_LENGTH] =
261            bytes
262                .try_into()
263                .map_err(|_| LicenseError::InvalidPublicKey {
264                    reason: "failed to convert key bytes".to_string(),
265                })?;
266
267        let verifying_key =
268            VerifyingKey::from_bytes(&key_bytes).map_err(|e| LicenseError::InvalidPublicKey {
269                reason: format!("invalid public key: {}", e),
270            })?;
271
272        Ok(Self { verifying_key })
273    }
274
275    /// Returns the public key as raw bytes.
276    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
277        self.verifying_key.to_bytes()
278    }
279
280    /// Returns the public key as a base64-encoded string.
281    pub fn to_base64(&self) -> String {
282        BASE64_STANDARD.encode(self.verifying_key.to_bytes())
283    }
284
285    /// Verifies a signature against the given data.
286    ///
287    /// # Arguments
288    ///
289    /// * `data` - The original data that was signed.
290    /// * `signature_bytes` - The signature to verify (64 bytes).
291    ///
292    /// # Returns
293    ///
294    /// `Ok(())` if the signature is valid, or an error if verification fails.
295    pub fn verify(&self, data: &[u8], signature_bytes: &[u8]) -> Result<()> {
296        if signature_bytes.len() != SIGNATURE_LENGTH {
297            return Err(LicenseError::InvalidSignature);
298        }
299
300        let sig_bytes: [u8; SIGNATURE_LENGTH] = signature_bytes
301            .try_into()
302            .map_err(|_| LicenseError::InvalidSignature)?;
303
304        let signature = Signature::from_bytes(&sig_bytes);
305
306        self.verifying_key
307            .verify(data, &signature)
308            .map_err(|_| LicenseError::InvalidSignature)
309    }
310
311    /// Verifies a base64-encoded signature against the given data.
312    ///
313    /// # Arguments
314    ///
315    /// * `data` - The original data that was signed.
316    /// * `signature_base64` - The base64-encoded signature.
317    ///
318    /// # Returns
319    ///
320    /// `Ok(())` if the signature is valid, or an error if verification fails.
321    pub fn verify_base64(&self, data: &[u8], signature_base64: &str) -> Result<()> {
322        let signature_bytes = BASE64_STANDARD
323            .decode(signature_base64)
324            .map_err(|_| LicenseError::InvalidSignature)?;
325
326        self.verify(data, &signature_bytes)
327    }
328}
329
330// =============================================================================
331// Utility Functions
332// =============================================================================
333
334/// Generates a new key pair and returns the keys as base64 strings.
335///
336/// This is a convenience function for quickly generating keys.
337///
338/// # Returns
339///
340/// A tuple of (private_key_base64, public_key_base64).
341///
342/// # Example
343///
344/// ```
345/// use rust_license_key::crypto::generate_key_pair_base64;
346///
347/// let (private_key, public_key) = generate_key_pair_base64().expect("Key generation failed");
348/// println!("Private (keep secret): {}", private_key);
349/// println!("Public (embed in app): {}", public_key);
350/// ```
351pub fn generate_key_pair_base64() -> Result<(String, String)> {
352    let key_pair = KeyPair::generate()?;
353    Ok((key_pair.private_key_base64(), key_pair.public_key_base64()))
354}
355
356#[cfg(test)]
357mod tests {
358    use super::*;
359
360    #[test]
361    fn test_key_pair_generation() {
362        let key_pair = KeyPair::generate().expect("Key generation should succeed");
363
364        // Verify key lengths
365        assert_eq!(key_pair.private_key_bytes().len(), SECRET_KEY_LENGTH);
366        assert_eq!(key_pair.public_key().to_bytes().len(), PUBLIC_KEY_LENGTH);
367    }
368
369    #[test]
370    fn test_key_pair_from_private_key() {
371        let original = KeyPair::generate().expect("Key generation should succeed");
372        let private_key_base64 = original.private_key_base64();
373
374        let restored =
375            KeyPair::from_private_key_base64(&private_key_base64).expect("Should restore key pair");
376
377        // Public keys should match
378        assert_eq!(
379            original.public_key().to_bytes(),
380            restored.public_key().to_bytes()
381        );
382    }
383
384    #[test]
385    fn test_public_key_from_base64() {
386        let key_pair = KeyPair::generate().expect("Key generation should succeed");
387        let public_key_base64 = key_pair.public_key_base64();
388
389        let public_key =
390            PublicKey::from_base64(&public_key_base64).expect("Should parse public key");
391
392        assert_eq!(public_key.to_bytes(), key_pair.public_key().to_bytes());
393    }
394
395    #[test]
396    fn test_sign_and_verify() {
397        let key_pair = KeyPair::generate().expect("Key generation should succeed");
398        let data = b"This is the license payload data";
399
400        // Sign with private key
401        let signature = key_pair.sign(data);
402
403        // Verify with public key
404        let public_key = key_pair.public_key();
405        public_key
406            .verify(data, &signature)
407            .expect("Signature should be valid");
408    }
409
410    #[test]
411    fn test_sign_and_verify_base64() {
412        let key_pair = KeyPair::generate().expect("Key generation should succeed");
413        let data = b"This is the license payload data";
414
415        // Sign with private key
416        let signature_base64 = key_pair.sign_base64(data);
417
418        // Verify with public key
419        let public_key = key_pair.public_key();
420        public_key
421            .verify_base64(data, &signature_base64)
422            .expect("Signature should be valid");
423    }
424
425    #[test]
426    fn test_verify_invalid_signature() {
427        let key_pair = KeyPair::generate().expect("Key generation should succeed");
428        let data = b"This is the license payload data";
429        let wrong_data = b"This is different data";
430
431        // Sign correct data
432        let signature = key_pair.sign(data);
433
434        // Verify against wrong data should fail
435        let public_key = key_pair.public_key();
436        assert!(public_key.verify(wrong_data, &signature).is_err());
437    }
438
439    #[test]
440    fn test_verify_with_wrong_key() {
441        let key_pair_1 = KeyPair::generate().expect("Key generation should succeed");
442        let key_pair_2 = KeyPair::generate().expect("Key generation should succeed");
443        let data = b"This is the license payload data";
444
445        // Sign with key_pair_1
446        let signature = key_pair_1.sign(data);
447
448        // Verify with key_pair_2 should fail
449        let public_key_2 = key_pair_2.public_key();
450        assert!(public_key_2.verify(data, &signature).is_err());
451    }
452
453    #[test]
454    fn test_invalid_private_key_length() {
455        let invalid_key = BASE64_STANDARD.encode(vec![0u8; 16]); // Wrong length
456        let result = KeyPair::from_private_key_base64(&invalid_key);
457        assert!(result.is_err());
458    }
459
460    #[test]
461    fn test_invalid_public_key_length() {
462        let invalid_key = BASE64_STANDARD.encode(vec![0u8; 16]); // Wrong length
463        let result = PublicKey::from_base64(&invalid_key);
464        assert!(result.is_err());
465    }
466
467    #[test]
468    fn test_invalid_base64_encoding() {
469        let result = KeyPair::from_private_key_base64("not valid base64!!!");
470        assert!(result.is_err());
471
472        let result = PublicKey::from_base64("not valid base64!!!");
473        assert!(result.is_err());
474    }
475
476    #[test]
477    fn test_generate_key_pair_base64_convenience() {
478        let (private_key, public_key) =
479            generate_key_pair_base64().expect("Key generation should succeed");
480
481        // Verify we can recreate the key pair from the private key
482        let key_pair = KeyPair::from_private_key_base64(&private_key)
483            .expect("Should create key pair from private key");
484
485        // And the public key matches
486        assert_eq!(key_pair.public_key_base64(), public_key);
487    }
488}