Security Model
Licenz uses industry-standard cryptographic techniques to ensure licenses cannot be forged, modified, or misused. This page explains the security architecture and how to leverage its protections in your applications.
Cryptographic Signatures
Every license is cryptographically signed using your private key. Only the corresponding public key can verify the signature, ensuring licenses can only be created by you.
(keep secret)
customers
(embedded in app)
Supported Algorithms
Licenz supports two signature algorithms, both offering strong security guarantees:
| Algorithm | Key Size | Signature Size | Best For |
|---|---|---|---|
| Ed25519 | 256 bits | 64 bytes | Modern applications, smaller license files, faster verification |
| RSA-2048/4096 | 2048/4096 bits | 256/512 bytes | Compatibility with older systems, regulatory compliance |
Recommendation
We recommend Ed25519 for new products. It provides equivalent security to RSA-3072 with significantly faster verification and smaller signatures.
Signature Verification
use licenz::{License, Verifier, PublicKey, SignatureAlgorithm};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load your public key (Ed25519 or RSA)
let public_key = PublicKey::from_pem(include_str!("public_key.pem"))?;
// Create verifier - algorithm is auto-detected from key
let verifier = Verifier::new(public_key);
let license = License::from_key("LIC-XXXX-XXXX-XXXX-XXXX")?;
// Verify cryptographic signature
match verifier.verify(&license) {
Ok(result) => {
println!("Signature valid: {}", result.signature_valid());
println!("Algorithm: {:?}", result.algorithm()); // Ed25519 or RSA
println!("Key ID: {}", result.key_id());
}
Err(e) => {
eprintln!("Signature verification failed: {}", e);
}
}
Ok(())
} Offline Validation
One of Licenz's core strengths is the ability to validate licenses completely offline. The cryptographic signature contains all information needed to verify authenticity without contacting any server.
How Offline Validation Works
- Embed public key - Include your public key in your application at compile time
- Parse license - Decode the license key to extract the signed data
- Verify signature - Use the public key to verify the signature is valid
- Check claims - Validate expiration, features, and other constraints
use licenz::{License, Verifier, PublicKey};
// Embed the public key at compile time - no network needed
const PUBLIC_KEY_PEM: &str = include_str!("public_key.pem");
fn validate_offline(license_key: &str) -> Result<bool, Box<dyn std::error::Error>> {
let public_key = PublicKey::from_pem(PUBLIC_KEY_PEM)?;
let verifier = Verifier::new(public_key);
let license = License::from_key(license_key)?;
// Full validation happens locally
let result = verifier.verify(&license)?;
// Check signature, expiration, and features - all offline
Ok(result.signature_valid()
&& result.is_active()
&& result.has_feature("pro"))
} Benefits of Offline Validation
- No internet required - Works in air-gapped environments
- Zero latency - Instant validation with no network round-trip
- Privacy - No usage data sent to external servers
- Reliability - No dependency on server availability
Anti-Tampering Protections
Beyond signature verification, Licenz includes multiple layers of tamper detection to identify modified or corrupted licenses.
Integrity Checks
use licenz::{License, Verifier, IntegrityCheck};
let verifier = Verifier::builder()
.public_key(public_key)
.integrity_checks(vec![
IntegrityCheck::LicenseSignature, // Verify cryptographic signature
IntegrityCheck::LicenseStructure, // Validate JSON structure
IntegrityCheck::FeatureConsistency, // Check feature references
IntegrityCheck::TimestampOrder, // issued_at < expires_at
])
.build()?;
let result = verifier.verify(&license)?;
// Get detailed integrity report
for check in result.integrity_checks() {
println!("{}: {}", check.name(), if check.passed() { "PASS" } else { "FAIL" });
} What's Protected
- License data - Any modification invalidates the signature
- Feature flags - Cannot be added or modified without re-signing
- Expiration dates - Tampering with dates is cryptographically detectable
- Hardware bindings - Fingerprint is part of the signed data
- Metadata - Custom fields are included in signature computation
Important: Secure Your Private Key
The security of your licensing system depends entirely on keeping your private key secret. Never embed the private key in your application or share it publicly. Store it securely and rotate it periodically.
Clock Protection
A common attack vector is manipulating the system clock to extend expired licenses. Licenz provides several mechanisms to detect and mitigate clock tampering.
Time Sources
Licenz can cross-reference multiple time sources to detect manipulation:
- System clock - The primary time source
- Build timestamp - When your application was compiled
- License issue time - When the license was created
- Last validation time - Persisted from previous runs
- Network time - Optional NTP check when online
use licenz::{License, Verifier, ClockSource};
// Use multiple time sources for tamper detection
let verifier = Verifier::builder()
.public_key(public_key)
.clock_source(ClockSource::System) // System clock
.clock_source(ClockSource::BuildTime) // Compile timestamp
.clock_source(ClockSource::LicenseIssueTime) // License issue date
.clock_tolerance(Duration::days(1)) // Allow 1 day drift
.build()?;
match verifier.verify(&license) {
Ok(result) => {
if result.clock_tampering_detected() {
println!("Warning: System clock may have been manipulated");
println!("System time: {:?}", result.system_time());
println!("Expected minimum: {:?}", result.minimum_time());
}
}
Err(e) => eprintln!("Validation error: {}", e),
} Clock Attack Scenarios
| Attack | Detection Method | Response |
|---|---|---|
| Set clock to past date | Time before build timestamp | Reject or warn |
| Set clock before license issue | Time before issued_at | Reject validation |
| Clock jumped backward | Time before last validation | Configurable response |
Key Management
Proper key management is essential for maintaining security.
Key Generation
# Generate Ed25519 key pair
licenz keys generate --algorithm ed25519
# Generate RSA key pair
licenz keys generate --algorithm rsa --bits 4096
# Output:
# Private key saved to: private_key.pem
# Public key saved to: public_key.pem
# Key ID: key_abc123 Key Rotation
Licenz supports key rotation to maintain security over time. When you rotate keys:
- Generate a new key pair
- Add the new public key to your application (alongside the old one)
- Start signing new licenses with the new private key
- Existing licenses remain valid (verified by old public key)
- After transition period, remove the old public key
# Rotate to a new key
licenz keys rotate --new-algorithm ed25519
# List all active keys
licenz keys list
# Deprecate old key (new licenses won't use it)
licenz keys deprecate key_old123 Security Best Practices
- Use Ed25519 for new deployments - faster and equally secure
- Embed public key at compile time - prevents runtime substitution
- Enable all integrity checks - multiple layers of protection
- Use hardware binding for high-value software
- Implement clock protection for time-limited licenses
- Rotate keys periodically - annually or after any suspected compromise
- Monitor for anomalies - unusual activation patterns may indicate piracy
Security audit? Licenz's cryptographic implementation has been reviewed by third-party security researchers. Contact us for the full audit report.
Next Steps
- Add hardware binding for additional protection
- Set up offline activation
- Key management API reference
- CLI key commands