Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
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

About

No description, website, or topics provided.

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages