An open API service indexing awesome lists of open source software.

https://github.com/jiri-otoupal/licensingpy


https://github.com/jiri-otoupal/licensingpy

Last synced: about 1 month ago
JSON representation

Awesome Lists containing this project

README

          

# ๐Ÿ” LicensingPy - Professional Offline Licensing System

[![PyPI version](https://badge.fury.io/py/licensingpy.svg)](https://badge.fury.io/py/licensingpy)
[![Python Support](https://img.shields.io/pypi/pyversions/licensingpy.svg)](https://pypi.org/project/licensingpy/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Downloads](https://pepy.tech/badge/licensingpy)](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.txt

LICENSE 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 string

def 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_licenses

def 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 LicenseManager

class 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 preseed

def 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 file

Options:
-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 string

Options:
--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 preseed

def 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 False

if __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!**