librnxengine 1.1.0

implement robust software licensing, activation, and validation systems.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
use crate::error::LicenseError;
use base64::engine::Engine;
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use rand::RngCore;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use tracing::{debug, error, info};
use zeroize::{Zeroize, ZeroizeOnDrop};

/// Private key wrapper with zeroization for secure memory management.
///
/// This struct wraps the Ed25519 signing key and ensures that sensitive
/// key material is securely wiped from memory when the struct is dropped.
/// The `ZeroizeOnDrop` trait guarantees that memory is cleared even on panic.
///
/// # Security Features
/// - Automatic memory zeroization on drop
/// - Protection against memory dump attacks
/// - Secure random key generation
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct PrivateKey(#[zeroize(skip)] SigningKey);

impl PrivateKey {
    /// Creates a new private key using cryptographically secure random generation.
    ///
    /// Uses the operating system's secure random number generator to create
    /// a 256-bit (32-byte) Ed25519 private key suitable for digital signatures.
    ///
    /// # Returns
    /// A new `PrivateKey` instance with securely generated key material.
    ///
    /// # Example
    /// ```rust
    /// let private_key = PrivateKey::new();
    /// ```
    pub fn new() -> Self {
        let mut csprng = OsRng;
        let mut bytes = [0u8; 32];
        csprng.fill_bytes(&mut bytes);
        let signing_key = SigningKey::from_bytes(&bytes);
        Self(signing_key)
    }

    /// Creates a private key from raw byte array.
    ///
    /// Validates that the input is exactly 32 bytes (256 bits) as required
    /// by the Ed25519 algorithm. Returns an error if the length is incorrect.
    ///
    /// # Parameters
    /// - `bytes`: Byte slice containing the private key (must be 32 bytes)
    ///
    /// # Returns
    /// - `Ok(PrivateKey)`: Successfully created private key
    /// - `Err(LicenseError)`: If bytes length is not 32 bytes
    ///
    /// # Security Warning
    /// The input bytes must be cryptographically secure random data.
    /// Do not use predictable or low-entropy sources for key generation.
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, LicenseError> {
        if bytes.len() != 32 {
            error!("Invalid private key length: {}", bytes.len());
            return Err(LicenseError::CryptoError(
                "Invalid private key length. Must be 32 bytes".to_string(),
            ));
        }
        let signing_key = SigningKey::from_bytes(bytes.try_into().map_err(|_| {
            LicenseError::CryptoError("Failed to convert bytes to array".to_string())
        })?);
        Ok(Self(signing_key))
    }

    /// Signs a message using the private key.
    ///
    /// Creates an Ed25519 digital signature for the given message.
    /// The signature can be verified using the corresponding public key.
    ///
    /// # Parameters
    /// - `message`: Byte slice containing the data to sign
    ///
    /// # Returns
    /// - `Ok(Signature)`: 64-byte Ed25519 signature
    /// - `Err(LicenseError)`: If signing operation fails
    ///
    /// # Example
    /// ```rust
    /// let signature = private_key.sign(b"license data")?;
    /// ```
    pub fn sign(&self, message: &[u8]) -> Result<Signature, LicenseError> {
        let signature = self.0.sign(message);
        debug!("Signed {} bytes message", message.len());
        Ok(signature)
    }
}

/// Public key for signature verification.
///
/// This struct represents a 32-byte Ed25519 public key used to verify
/// digital signatures created by the corresponding private key.
/// It supports serialization/deserialization and base64 encoding.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicKey(#[serde(with = "serde_bytes")] [u8; 32]);

impl PublicKey {
    /// Verifies a digital signature against a message.
    ///
    /// Validates that the signature was created by the private key
    /// corresponding to this public key for the given message.
    ///
    /// # Parameters
    /// - `message`: Original message that was signed
    /// - `signature`: 64-byte Ed25519 signature to verify
    ///
    /// # Returns
    /// - `Ok(())`: Signature is valid
    /// - `Err(LicenseError)`: Signature verification failed
    ///
    /// # Cryptographic Properties
    /// - Deterministic: Same input always produces same result
    /// - Strong: Resistant to forgery attacks
    /// - Fast: Verification completes in microseconds
    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), LicenseError> {
        let verifying_key = VerifyingKey::from_bytes(&self.0).map_err(|e| {
            error!("Invalid public key format: {}", e);
            LicenseError::CryptoError(format!("Invalid public key: {}", e))
        })?;
        verifying_key.verify(message, signature).map_err(|e| {
            error!("Signature verification failed: {}", e);
            LicenseError::SignatureValidationFailed
        })?;
        debug!("Signature verified successfully");
        Ok(())
    }

    /// Encodes the public key as a base64 string.
    ///
    /// Uses standard base64 encoding (RFC 4648) without line breaks.
    /// Suitable for transmission over text-based protocols or storage
    /// in configuration files.
    ///
    /// # Returns
    /// Base64-encoded string representation of the public key
    ///
    /// # Example Output
    /// "j2vh1L2w1qZ3pP8T9sR4xY7zA6bC5dE8fG2hJ3k"
    pub fn to_base64(&self) -> String {
        base64::engine::general_purpose::STANDARD.encode(self.0)
    }

    /// Decodes a public key from a base64 string.
    ///
    /// Validates that the decoded data is exactly 32 bytes.
    /// Returns an error if the base64 is malformed or length is incorrect.
    ///
    /// # Parameters
    /// - `s`: Base64-encoded public key string
    ///
    /// # Returns
    /// - `Ok(PublicKey)`: Successfully decoded public key
    /// - `Err(LicenseError)`: If decoding fails or length is wrong
    pub fn from_base64(s: &str) -> Result<Self, LicenseError> {
        let bytes = base64::engine::general_purpose::STANDARD
            .decode(s)
            .map_err(|e| {
                error!("Failed to decode base64 public key: {}", e);
                LicenseError::CryptoError(format!("Base64 decode error: {}", e))
            })?;
        if bytes.len() != 32 {
            error!(
                "Invalid public key length after base64 decode: {}",
                bytes.len()
            );
            return Err(LicenseError::CryptoError(
                "Invalid public key length".to_string(),
            ));
        }
        let mut key = [0u8; 32];
        key.copy_from_slice(&bytes);
        Ok(Self(key))
    }

    /// Returns a reference to the raw public key bytes.
    ///
    /// Useful for cryptographic operations that require byte arrays.
    ///
    /// # Returns
    /// Reference to the 32-byte public key array
    pub fn as_bytes(&self) -> &[u8; 32] {
        &self.0
    }

    /// Returns a copy of the public key bytes.
    ///
    /// Allocates a new array containing the key bytes.
    /// Use `as_bytes()` when you only need a reference.
    ///
    /// # Returns
    /// 32-byte array containing the public key
    pub fn to_bytes(&self) -> [u8; 32] {
        self.0
    }
}

/// Cryptographic key pair for license operations.
///
/// Contains both private and public keys for asymmetric cryptography.
/// The private key is protected with zeroization for secure memory management.
/// This is the primary structure for license signing and verification.
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct KeyPair {
    #[zeroize(skip)]
    pub public: PublicKey,
    pub private: PrivateKey,
}

impl KeyPair {
    /// Generates a new Ed25519 key pair using secure random generation.
    ///
    /// Creates a cryptographically strong key pair suitable for
    /// production use. The private key is generated using the
    /// operating system's secure random number generator.
    ///
    /// # Returns
    /// A new `KeyPair` with randomly generated private and public keys.
    ///
    /// # Security Considerations
    /// - Uses `OsRng` which is cryptographically secure
    /// - Private key memory is zeroized on drop
    /// - Public key can be safely distributed
    pub fn generate() -> Self {
        info!("Generating new Ed25519 key pair");
        let mut csprng = OsRng;
        let mut bytes = [0u8; 32];
        csprng.fill_bytes(&mut bytes);
        let signing_key = SigningKey::from_bytes(&bytes);
        let verifying_key = signing_key.verifying_key();
        Self {
            private: PrivateKey(signing_key),
            public: PublicKey(verifying_key.to_bytes()),
        }
    }

    /// Derives a key pair from a 32-byte seed.
    ///
    /// Uses deterministic key generation where the same seed always
    /// produces the same key pair. Useful for key derivation from
    /// master keys or password-based key generation (with proper KDF).
    ///
    /// # Parameters
    /// - `seed`: 32-byte seed for deterministic key generation
    ///
    /// # Returns
    /// - `Ok(KeyPair)`: Key pair derived from the seed
    /// - `Err(LicenseError)`: If seed length is not 32 bytes
    ///
    /// # Security Warning
    /// The seed must have high entropy (cryptographically random).
    /// Do not use passwords directly as seeds without proper KDF.
    pub fn from_seed(seed: &[u8]) -> Result<Self, LicenseError> {
        debug!("Generating key pair from seed");
        if seed.len() != 32 {
            return Err(LicenseError::CryptoError(
                "Seed must be 32 bytes".to_string(),
            ));
        }
        let seed_bytes: [u8; 32] = seed.try_into().map_err(|_| {
            LicenseError::CryptoError("Failed to convert seed to array".to_string())
        })?;
        let signing_key = SigningKey::from_bytes(&seed_bytes);
        let verifying_key = signing_key.verifying_key();
        Ok(Self {
            private: PrivateKey(signing_key),
            public: PublicKey(verifying_key.to_bytes()),
        })
    }

    /// Saves the public key (only) to a JSON file.
    ///
    /// Creates a new file containing the public key in base64 format
    /// along with metadata. The private key is NOT saved to disk.
    ///
    /// # Parameters
    /// - `path`: Filesystem path where to save the public key
    ///
    /// # Returns
    /// - `Ok(())`: File saved successfully
    /// - `Err(LicenseError)`: If file operations or serialization fails
    ///
    /// # File Format
    /// ```json
    /// {
    ///   "public_key": "j2vh1L2w1qZ3pP8T9sR4xY7zA6bC5dE8fG2hJ3k...",
    ///   "algorithm": "ed25519",
    ///   "created_at": "2024-01-01T00:00:00Z"
    /// }
    /// ```
    ///
    /// # Security Note
    /// Only the public key is saved. Private keys should be stored
    /// in secure hardware or encrypted key stores.
    pub fn save_to_file(&self, path: &std::path::Path) -> Result<(), LicenseError> {
        use std::fs;
        use std::io::Write;

        debug!("Saving key pair to: {:?}", path);

        let data = serde_json::json!({
            "public_key": self.public.to_base64(),
            "algorithm": "ed25519",
            "created_at": chrono::Utc::now().to_rfc3339(),
        });

        let json = serde_json::to_string_pretty(&data)
            .map_err(|e| LicenseError::SerializationError(e.to_string()))?;

        let mut file = fs::OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(path)?;

        file.write_all(json.as_bytes())?;
        file.sync_all()?;

        info!("Key pair saved to {:?}", path);
        Ok(())
    }

    /// Loads a public key from a JSON file.
    ///
    /// Parses a key file created by `save_to_file()` and extracts
    /// the public key. Validates the file format and key encoding.
    ///
    /// # Parameters
    /// - `path`: Filesystem path to the key file
    ///
    /// # Returns
    /// - `Ok(PublicKey)`: Loaded public key
    /// - `Err(LicenseError)`: If file operations or parsing fails
    ///
    /// # Use Case
    /// Used by license validators to load the public key for
    /// signature verification without accessing private keys.
    pub fn load_public_from_file(path: &std::path::Path) -> Result<PublicKey, LicenseError> {
        debug!("Loading public key from: {:?}", path);

        let data = std::fs::read_to_string(path)?;
        let json: serde_json::Value = serde_json::from_str(&data)
            .map_err(|e| LicenseError::SerializationError(e.to_string()))?;

        let pub_key_b64 = json["public_key"].as_str().ok_or_else(|| {
            error!("Invalid key file format: missing public_key field");
            LicenseError::CryptoError("Invalid key file format".to_string())
        })?;

        PublicKey::from_base64(pub_key_b64)
    }
}

// Note: Ed25519 is chosen for its security properties and performance:
// - Strong security (128-bit security level)
// - Fast signing and verification
// - Small signature size (64 bytes)
// - Deterministic signatures (no randomness needed during signing)
// - Built-in resilience to several attack vectors

// Note: The `zeroize` crate provides critical security for private keys:
// - Ensures key material is wiped from memory when no longer needed
// - Protects against memory scraping attacks
// - Works even in panics due to `ZeroizeOnDrop` trait
// - The `#[zeroize(skip)]` attribute prevents zeroization of public data
//   that doesn't contain secrets

// Note: Base64 encoding is used for text-based storage and transmission:
// - Standard encoding (RFC 4648) for interoperability
// - No line breaks for compact representation
// - Safe for JSON, XML, and other text formats
// - Readable and debuggable

// Note: Key management best practices:
// 1. Private keys should NEVER be saved to disk in plain text
// 2. Public keys can be freely distributed for verification
// 3. Use hardware security modules (HSMs) for production private keys
// 4. Implement key rotation policies for long-lived systems
// 5. Store creation timestamps for audit purposes

// Note: Error handling strategy:
// - Detailed error messages for debugging (with tracing)
// - Specific error variants for different failure modes
// - Early validation of input parameters
// - Secure cleanup on error (thanks to zeroize)

// Example usage patterns:
// ```
// // Generate new key pair
// let keypair = KeyPair::generate();
//
// // Sign a license
// let signature = keypair.private.sign(license_data)?;
//
// // Save public key for distribution
// keypair.save_to_file(Path::new("public_key.json"))?;
//
// // Load public key for verification
// let public_key = KeyPair::load_public_from_file(Path::new("public_key.json"))?;
//
// // Verify a signature
// public_key.verify(message, &signature)?;
// ```