Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
"""
AbzacFixer License System — offline HMAC-signed keys.
Keys encode: expiry date + signature. No server needed.
Key format: AF-YYYYMMDD-XXXXX-XXXXX-XXXXX
AF = prefix
YYYYMMDD = expiry date
XXXXX x3 = HMAC signature (first 15 chars, base32)
Usage:
generate_key(secret, days=30) -> key string
validate_key(secret, key) -> (valid: bool, message: str, days_left: int)
"""
import hmac
import hashlib
import base64
import datetime
import os
import json
# Where the activated license is stored on user's machine
LICENSE_FILE = os.path.join(os.path.expanduser("~"), ".abzacfixer_license")
# Blacklist file (admin puts revoked keys here)
BLACKLIST_FILE = os.path.join(os.path.expanduser("~"), ".abzacfixer_blacklist.json")
def _sign(secret: str, data: str) -> str:
"""Generate HMAC-SHA256 signature, return as uppercase base32 (15 chars)."""
h = hmac.new(secret.encode(), data.encode(), hashlib.sha256).digest()
b32 = base64.b32encode(h).decode().replace("=", "")
return b32[:15].upper()
def generate_key(secret: str, days: int = 30) -> str:
"""Generate a license key valid for N days from now."""
expiry = datetime.date.today() + datetime.timedelta(days=days)
date_str = expiry.strftime("%Y%m%d")
sig = _sign(secret, f"ABZAC-{date_str}")
# Format: AF-YYYYMMDD-XXXXX-XXXXX-XXXXX
key = f"AF-{date_str}-{sig[0:5]}-{sig[5:10]}-{sig[10:15]}"
return key
def validate_key(secret: str, key: str) -> tuple:
"""
Validate a license key.
Returns (is_valid, message, days_left)
"""
key = key.strip().upper()
# Parse format
parts = key.split("-")
if len(parts) != 5 or parts[0] != "AF":
return False, "INVALID_FORMAT: Key must be AF-XXXXXXXX-XXXXX-XXXXX-XXXXX", 0
date_str = parts[1]
sig_from_key = parts[2] + parts[3] + parts[4]
# Verify date format
try:
expiry = datetime.datetime.strptime(date_str, "%Y%m%d").date()
except ValueError:
return False, "INVALID_DATE: Key contains invalid date.", 0
# Verify signature
expected_sig = _sign(secret, f"ABZAC-{date_str}")
if not hmac.compare_digest(sig_from_key, expected_sig):
return False, "INVALID_SIG: Key signature is invalid. Possible forgery.", 0
# Check blacklist
if is_blacklisted(key):
return False, "REVOKED: This key has been revoked.", 0
# Check expiry
today = datetime.date.today()
days_left = (expiry - today).days
if days_left < 0:
return False, f"EXPIRED: Key expired {abs(days_left)} days ago.", 0
return True, f"OK: Valid for {days_left} more days.", days_left
def activate_license(key: str):
"""Save activated key to disk."""
with open(LICENSE_FILE, 'w') as f:
f.write(key.strip().upper())
def get_saved_license() -> str:
"""Read saved license from disk."""
if os.path.exists(LICENSE_FILE):
with open(LICENSE_FILE, 'r') as f:
return f.read().strip()
return ""
def remove_license():
"""Remove saved license."""
if os.path.exists(LICENSE_FILE):
os.remove(LICENSE_FILE)
def is_blacklisted(key: str) -> bool:
"""Check if key is in blacklist."""
if not os.path.exists(BLACKLIST_FILE):
return False
try:
with open(BLACKLIST_FILE, 'r') as f:
bl = json.load(f)
return key.strip().upper() in [k.upper() for k in bl]
except:
return False
def add_to_blacklist(key: str):
"""Add key to blacklist (admin use)."""
bl = []
if os.path.exists(BLACKLIST_FILE):
try:
with open(BLACKLIST_FILE, 'r') as f:
bl = json.load(f)
except:
pass
key = key.strip().upper()
if key not in bl:
bl.append(key)
with open(BLACKLIST_FILE, 'w') as f:
json.dump(bl, f, indent=2)
def remove_from_blacklist(key: str):
"""Remove key from blacklist."""
if not os.path.exists(BLACKLIST_FILE):
return
try:
with open(BLACKLIST_FILE, 'r') as f:
bl = json.load(f)
bl = [k for k in bl if k.upper() != key.strip().upper()]
with open(BLACKLIST_FILE, 'w') as f:
json.dump(bl, f, indent=2)
except:
pass