https://github.com/jiri-otoupal/licensingpy
https://github.com/jiri-otoupal/licensingpy
Last synced: about 1 month ago
JSON representation
- Host: GitHub
- URL: https://github.com/jiri-otoupal/licensingpy
- Owner: jiri-otoupal
- License: mit
- Created: 2025-08-21T11:57:26.000Z (about 2 months ago)
- Default Branch: master
- Last Pushed: 2025-08-22T09:00:55.000Z (about 2 months ago)
- Last Synced: 2025-08-28T14:17:47.828Z (about 2 months ago)
- Language: Python
- Size: 79.1 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ๐ LicensingPy - Professional Offline Licensing System
[](https://badge.fury.io/py/licensingpy)
[](https://pypi.org/project/licensingpy/)
[](https://opensource.org/licenses/MIT)
[](https://pepy.tech/project/licensingpy)A professional, secure offline licensing solution with beautiful CLI, ECDSA signatures, and hardware fingerprinting.
## โจ Features
- ๐ **ECDSA P-256 Signatures** - Cryptographically secure license verification
- ๐ฅ๏ธ **Hardware Fingerprinting** - Bind licenses to specific machines (MAC, disk, CPU, system, composite)
- ๐งฉ **Component-Based Licensing** - Separate licenses for different modules/components
- ๐ฑ **Secure Preseed System** - File-based secret management with SHA-256 hashing
- ๐จ **Beautiful Rich CLI** - Colorful, interactive command-line interface with progress bars
- ๐ง **Cross-Platform** - Windows, Linux, macOS with native fallbacks
- ๐ **Offline Operation** - No internet connection required for license operations
- ๐ก๏ธ **Tamper Resistant** - Licenses cannot be modified, copied, or forged
- โก **Auto-Verification** - Automatic license discovery and batch validation
- ๐ฆ **Zero Dependencies** - Optional hardware detection libraries with native fallbacks
- ๐งช **Comprehensive Tests** - 111+ test cases with high coverage
- ๐ **Complete Documentation** - Detailed guides and API documentation## ๐ฆ Installation
### Using pip (Recommended)
```bash
pip install licensingpy
```### Using Poetry
```bash
poetry add licensingpy
```### With Optional Hardware Detection
For enhanced hardware detection capabilities:
```bash
pip install "licensingpy[hardware]"
# or
poetry add licensingpy -E hardware
```### Development Installation
```bash
git clone https://github.com/licensingpy/licensingpy.git
cd licensingpy
poetry install --with dev,test
```## ๐ Quick Start Guide
### Step 1: Generate Preseed File
The preseed file contains secret content that secures your licenses:
```bash
licensingpy generate-preseed --project-name "MyAwesomeApp" --description "Production preseed for MyApp" --output my_app_preseed.json
```**Output:**
```
โ Preseed file generated: my_app_preseed.json
โ Secret length: 64 characters
โ File size: 285 bytes๐ SECURITY NOTES:
โข Keep my_app_preseed.json secure and confidential
โข Do NOT commit my_app_preseed.json to version control
โข Back up my_app_preseed.json safely
```### Step 2: Generate Cryptographic Keys
```bash
licensingpy generate-keys --format json --output my_app_keys.json
```**Output:**
```
โ Key pair generated successfully
โ Keys saved to my_app_keys.json๐ SECURITY NOTES:
- Keep the private key secure and confidential
- The public key can be distributed with your application
```### Step 3: Generate a License
```bash
licensingpy generate-license --private-key my_app_keys.json --preseed-file my_app_preseed.json --app-name "MyAwesomeApp" --version "2.1.0" --component-name "CoreEngine" --customer "Acme Corporation" --expires "2025-12-31" --output customer_license.txt
```**Output:**
```
โ Loaded preseed from: my_app_preseed.json
Generating license (expires: 2025-12-31)...
Hardware fingerprint (composite): 4e120bb4a65e...License generated successfully!
License Details:
Fingerprint Type: composite
Expiry Date: 2025-12-31
Component Name: CoreEngine
App Name: MyAwesomeApp
App Version: 2.1.0
Customer: Acme Corporationโ License saved to: customer_license.txt
```### Step 4: Verify the License
```bash
licensingpy verify-license --public-key my_app_keys.json --preseed-file my_app_preseed.json --license customer_license.txt --verbose
```**Output:**
```
โ Loaded preseed from: my_app_preseed.json
โ Loaded license from: customer_license.txtLICENSE IS VALID AND ACTIVE
โ Signature verification: PASSED
โ Hardware fingerprint: MATCHED
โ License expiry: NOT EXPIRED
โ Component verification: PASSED
```## ๐ป Using in Your Python Code
### Basic License Verification
> **๐ Security Note**: For production applications, always hardcode your preseed string instead of loading it from a file. This ensures only licenses created with your specific preseed can be verified by your application.
```python
from licensing import verify_license_with_preseed, LicenseManager# RECOMMENDED: Use hardcoded credentials (most secure)
# Your application's secret credentials (hardcode these, don't load from files)
PUBLIC_KEY = "LS0tLS1CRUdJTi..." # Your actual public key here
APP_PRESEED = "my-secret-app-preseed-2024" # Your secret preseed stringdef verify_license(license_string):
"""Verify a license string using hardcoded preseed."""
try:
# Use the convenience function with hardcoded preseed
license_data = verify_license_with_preseed(
license_string=license_string,
public_key=PUBLIC_KEY,
preseed=APP_PRESEED
)
print("โ License is valid!")
print(f"App: {license_data.get('app_name')}")
print(f"Component: {license_data.get('component_name')}")
print(f"Customer: {license_data.get('customer')}")
print(f"Expires: {license_data.get('expiry')}")
return True
except Exception as e:
print(f"โ License verification failed: {e}")
return False# Alternative: Using LicenseManager directly
def verify_license_direct(license_string):
"""Verify a license using LicenseManager directly."""
manager = LicenseManager(PUBLIC_KEY, APP_PRESEED)
return manager.verify_license(license_string)# Example usage
if __name__ == "__main__":
# Load license from file
with open('customer_license.txt', 'r') as f:
license_string = f.read().strip()
if verify_license(license_string):
print("๐ Application can start!")
else:
print("๐ซ Application access denied!")
```### Auto-Discovery License Verification
```python
from licensing import auto_verify_licensesdef check_all_licenses():
"""Automatically find and verify all licenses in current directory."""
# Auto-discover and verify licenses
results = auto_verify_licenses()
if "error" in results:
print(f"โ Error: {results['error']}")
return False
summary = results['summary']
print(f"๐ License Summary:")
print(f" Total found: {summary['total_licenses']}")
print(f" โ Valid: {summary['valid_count']}")
print(f" โ Invalid: {summary['invalid_count']}")
print(f" โฐ Expired: {summary['expired_count']}")
# Show valid licenses
for license_data in results['valid_licenses']:
print(f"\n๐ Valid License:")
print(f" App: {license_data.get('app_name', 'N/A')}")
print(f" Component: {license_data.get('component_name', 'N/A')}")
print(f" Customer: {license_data.get('customer', 'N/A')}")
print(f" Expires: {license_data.get('expiry', 'N/A')}")
return summary['valid_count'] > 0# Example usage
if __name__ == "__main__":
if check_all_licenses():
print("๐ Valid licenses found - application authorized!")
else:
print("๐ซ No valid licenses found - access denied!")
```### Component-Specific License Checking
```python
from licensing import LicenseManagerclass ComponentLicenseManager:
"""Manage licenses for different application components."""
def __init__(self, public_key, preseed):
# Use hardcoded credentials for security
self.public_key = public_key
self.preseed = preseed
self.manager = LicenseManager(self.public_key, self.preseed)
# Cache for verified licenses
self.verified_components = {}
def verify_component_license(self, license_file, required_component):
"""Verify license for a specific component."""
# Check cache first
if required_component in self.verified_components:
return self.verified_components[required_component]
try:
# Load license
with open(license_file, 'r') as f:
license_string = f.read().strip()
# Verify license
license_data = self.manager.verify_license(license_string)
# Check component match
license_component = license_data.get('component_name', '')
if license_component != required_component:
print(f"โ Component mismatch: need '{required_component}', got '{license_component}'")
return False
# Cache successful verification
self.verified_components[required_component] = license_data
print(f"โ Component '{required_component}' licensed to: {license_data.get('customer')}")
return True
except Exception as e:
print(f"โ Component '{required_component}' license verification failed: {e}")
return False
def require_license(self, component_name):
"""Decorator to require license for a component."""
def decorator(func):
def wrapper(*args, **kwargs):
if not self.verify_component_license('license.txt', component_name):
raise PermissionError(f"No valid license for component '{component_name}'")
return func(*args, **kwargs)
return wrapper
return decorator# Example usage with hardcoded credentials
PUBLIC_KEY = "LS0tLS1CRUdJTi..." # Your actual public key
APP_PRESEED = "my-secret-app-preseed-2024" # Your secret preseed
license_manager = ComponentLicenseManager(PUBLIC_KEY, APP_PRESEED)@license_manager.require_license('DatabaseEngine')
def access_database():
"""This function requires a valid DatabaseEngine license."""
print("๐๏ธ Accessing database with licensed engine...")
# Your database code here@license_manager.require_license('ReportGenerator')
def generate_reports():
"""This function requires a valid ReportGenerator license."""
print("๐ Generating reports with licensed engine...")
# Your reporting code here# Use the licensed functions
try:
access_database() # Requires DatabaseEngine license
generate_reports() # Requires ReportGenerator license
except PermissionError as e:
print(f"๐ซ Access denied: {e}")
```### Advanced License Validation
```python
from licensing import LicenseManager
from datetime import datetime, timedelta# Hardcoded credentials for security
PUBLIC_KEY = "LS0tLS1CRUdJTi..." # Your actual public key
APP_PRESEED = "my-secret-app-preseed-2024" # Your secret preseeddef advanced_license_check(license_file):
"""Advanced license validation with detailed reporting."""
try:
# Use hardcoded credentials
manager = LicenseManager(PUBLIC_KEY, APP_PRESEED)
# Load license
with open(license_file, 'r') as f:
license_string = f.read().strip()
# Get license info without full validation
license_info = manager.get_license_info(license_string)
print("๐ License Information:")
print(f" App: {license_info.get('app_name', 'N/A')}")
print(f" Version: {license_info.get('app_version', 'N/A')}")
print(f" Component: {license_info.get('component_name', 'N/A')}")
print(f" Customer: {license_info.get('customer', 'N/A')}")
print(f" Issued: {license_info.get('issued', 'N/A')}")
print(f" Expires: {license_info.get('expiry', 'N/A')}")
# Check expiry details
days_left = manager.get_days_until_expiry(license_string)
if days_left is not None:
if days_left > 0:
print(f" โฐ Days remaining: {days_left}")
if days_left <= 30:
print(" โ ๏ธ License expiring soon!")
else:
print(f" ๐ License expired {abs(days_left)} days ago")
# Show status details
status = license_info.get('status', {})
print(f"\n๐ Verification Status:")
print(f" Signature: {'โ VALID' if not status.get('signature_invalid') else 'โ INVALID'}")
print(f" Hardware: {'โ MATCH' if status.get('hardware_matches') else 'โ MISMATCH'}")
print(f" Expiry: {'โ ACTIVE' if not status.get('is_expired') else 'โ EXPIRED'}")
print(f" Preseed: {'โ VALID' if status.get('preseed_valid') else 'โ INVALID'}")
# Attempt full verification
print(f"\n๐ก๏ธ Full Verification:")
try:
verified_license = manager.verify_license(license_string)
print("โ LICENSE IS FULLY VALID AND ACTIVE")
return True
except Exception as e:
print(f"โ Full verification failed: {e}")
return False
except Exception as e:
print(f"โ License check failed: {e}")
return False# Example usage
if __name__ == "__main__":
is_valid = advanced_license_check(
license_file='customer_license.txt',
preseed_file='my_app_preseed.json',
public_key_file='my_app_keys.json'
)
if is_valid:
print("\n๐ Application startup authorized!")
else:
print("\n๐ซ Application startup denied!")
```## ๐ ๏ธ CLI Reference
### Generate Preseed File
```bash
licensingpy generate-preseed [OPTIONS]Options:
-o, --output PATH Output file (default: preseed.json)
-l, --length INTEGER Secret length in characters (default: 64)
--project-name TEXT Project name for metadata
--description TEXT Description for metadata
```### Generate Keys
```bash
licensingpy generate-keys [OPTIONS]Options:
-o, --output PATH Output file for keys
--format [json|text] Output format (default: text)
```### Generate License
```bash
licensingpy generate-license [OPTIONS]Required:
-k, --private-key PATH Private key file
-p, --preseed-file PATH Preseed fileOptions:
-e, --expires TEXT Expiry date (YYYY-MM-DD) or days (30d)
-f, --fingerprint-type Hardware fingerprint type
-t, --target-hardware PATH Generate for specific hardware
--app-name TEXT Application name
--version TEXT Application version
--customer TEXT Customer name
-c, --component-name TEXT Component/module name
-o, --output PATH Output license file
```### Verify License
```bash
licensingpy verify-license [OPTIONS]Required:
-k, --public-key PATH Public key file
-p, --preseed-file PATH Preseed file
-l, --license PATH License file or stringOptions:
--skip-hardware Skip hardware verification
--skip-expiry Skip expiry verification
-v, --verbose Show detailed information
```### Demo Workflow
```bash
licensingpy demo
```## ๐ File Structure
After following the quick start guide, you'll have:
```
your_project/
โโโ my_app_preseed.json # Secret preseed file (keep secure!)
โโโ my_app_keys.json # Public/private keys
โโโ customer_license.txt # Generated license
โโโ your_application.py # Your app with license verification
```## ๐ Security Best Practices
### 1. Preseed File Security
- โ **Keep preseed files secure** - treat like passwords
- โ **Never commit to version control** - add to .gitignore
- โ **Back up safely** - store in secure location
- โ **Use different preseeds** for different products/versions### 2. Key Management
- โ **Private keys** - Keep secret, use for license generation only
- โ **Public keys** - Can be distributed with your application
- โ **Separate environments** - Different keys for dev/staging/prod### 3. License Distribution
- โ **Unique licenses** - Generate separate license for each customer
- โ **Component isolation** - Use different component names for modules
- โ **Expiry dates** - Set appropriate expiration dates
- โ **Hardware binding** - Bind to customer's specific hardware### 4. Application Integration
- โ **Fail securely** - Deny access if license verification fails
- โ **Cache verification** - Avoid re-verifying same license repeatedly
- โ **Component separation** - Check licenses for specific features
- โ **User feedback** - Provide clear messages for license issues## ๐ Troubleshooting
### License Verification Fails
**Hardware Mismatch:**
```bash
# Skip hardware check for testing
licensingpy verify-license --skip-hardware ...
```**License Expired:**
```bash
# Check expiry date
licensingpy verify-license --verbose ...
```**Invalid Signature:**
- Ensure you're using the correct preseed file
- Verify the public key matches the private key used for generation### Auto-Discovery Issues
**No licenses found:**
- Ensure license files are in current directory
- Check file naming: `license.txt`, `*.license`, etc.**No keys found:**
- Ensure key files are present: `keys.json`, `public_key.txt`, etc.## ๐ Support
For issues and questions:
1. Check this README for common solutions
2. Review the example code above
3. Test with the demo command: `licensingpy demo`
4. Verify your file structure and permissions## ๐ฏ Example Project Structure
```python
# main.py - Your application entry point
from licensing import LicenseManager# Hardcoded credentials (most secure approach)
PUBLIC_KEY = "LS0tLS1CRUdJTi..." # Your actual public key
APP_PRESEED = "my-secret-app-preseed-2024" # Your secret preseeddef startup_license_check():
"""Check license before starting application."""
try:
# Use hardcoded credentials for security
manager = LicenseManager(PUBLIC_KEY, APP_PRESEED)
# Verify license
with open('license.txt', 'r') as f:
license_string = f.read().strip()
license_data = manager.verify_license(license_string)
print(f"โ Licensed to: {license_data.get('customer')}")
print(f"๐ฑ App: {license_data.get('app_name')} v{license_data.get('app_version')}")
print(f"๐งฉ Component: {license_data.get('component_name')}")
return True
except Exception as e:
print(f"โ License verification failed: {e}")
return Falseif __name__ == "__main__":
if startup_license_check():
print("๐ Starting application...")
# Your application code here
else:
print("๐ซ Application startup denied - invalid license")
exit(1)
```---
**๐ You're now ready to integrate secure offline licensing into your Python applications!**