# LicenseMaker.py
import os
import textwrap
import os
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad
from Cryptodome.Random import get_random_bytes
import base64
import logging
import time
import logging
from base64 import b32encode
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA256
from Cryptodome.Signature import pkcs1_15
from Cryptodome import Random
from Cryptodome.Util.number import bytes_to_long, long_to_bytes
from utils2 import readProductKey  # Ensure this import is correct
logger = logging.getLogger(__name__)
DEFAULT_RSA_KEY_SIZE = 2048
DEFAULT_RSA_PUBLIC_EXPONENT = 65537
DEFAULT_AES_KEY_SIZE = 256
_AES_POSSIBLE_SIZES = [128, 192, 256]
_PROTECTED_SOURCE_TEMPLATE = """from runtime import expose
exec(expose({encryptedContent}, {initializationVector}, {originalSize}))"""
_INJECTED_CODECLOSE_MODULES = ['runtime', 'errors']
def configure(productKey=None, verifyingPublicKey=None, encryptingKey=None, expectedProductIds=None,
              licenseIdSize=None, productIdSize=None,
              expirationTimeSize=None, hashSize=None):
    global _license, _productKey, _verifyingPublicKey, _encryptingKey, _expectedProductIds
    global _licenseIdSize, _productIdSize, _expirationTimeSize, _hashSize  # Declare as global
    _productKey = productKey
    _verifyingPublicKey = verifyingPublicKey
    _encryptingKey = encryptingKey
    _expectedProductIds = expectedProductIds
    _licenseIdSize = licenseIdSize
    _productIdSize = productIdSize
    _expirationTimeSize = expirationTimeSize
    _hashSize = hashSize
    logger.info("LicenseMaker configured with provided parameters.")
def encrypt_file(file_path, encrypting_key):
    logger.info(f"Encrypting file: {file_path}")
    try:
        with open(file_path, 'rb') as f:
            original_content = f.read()
        logger.debug(f"Original content size: {len(original_content)} bytes")
        iv = get_random_bytes(AES.block_size)
        logger.debug(f"Generated IV: {base64.b64encode(iv).decode('utf-8')}")
        cipher = AES.new(encrypting_key, AES.MODE_CBC, iv)
        padded_content = pad(original_content, AES.block_size)
        logger.debug(f"Padded content size: {len(padded_content)} bytes")
        encrypted_content = cipher.encrypt(padded_content)
        logger.debug(f"Encrypted content size: {len(encrypted_content)} bytes")

        logger.info("File encrypted successfully.")
        return encrypted_content, iv
    except Exception as e:
        logger.exception("Failed to encrypt the file.")
        raise e
def configureLicenseComputation(verifyingPublicKey, encryptingKey, expectedProductIds,
                                licenseIdSize, productIdSize, expirationTimeSize, hashSize):
    logger.info("Configuring license computation.")
    configuration = {
        'verifyingPublicKeyInstance': RSA.import_key(verifyingPublicKey),
        'licenseIdSize': licenseIdSize,
        'productIdSize': productIdSize,
        'expirationTimeSize': expirationTimeSize,
        'hashSize': hashSize,
        'expectedProductIds': expectedProductIds,
        'encryptingKeyString': b32encode(encryptingKey).decode('utf-8')  # Use Base32 for consistency
    }
    logger.debug(f"License computation configuration: {configuration}")
    return configuration
def _generateProbablePrime(exact_bits, randfunc, prime_filter=lambda x: True):
    from Cryptodome.Math.Numbers import Integer
    from Cryptodome.Math.Primality import test_probable_prime
    result = 0
    while result == 0:
        candidate = Integer.random(exact_bits=exact_bits, randfunc=randfunc) | 1  # Ensure it's odd
        if not prime_filter(candidate):
            continue
        if test_probable_prime(candidate, randfunc):
            result = candidate
    logger.debug(f"Generated probable prime: {candidate}")
    return candidate
def _generateRSAKey(bits, randfunc=None, e=65537):
    from Cryptodome.Math.Numbers import Integer
    if e % 2 == 0 or e < 3:
        logger.error("RSA public exponent must be a positive, odd integer larger than 2.")
        raise ValueError("RSA public exponent must be a positive, odd integer larger than 2.")
    if randfunc is None:
        randfunc = Random.get_random_bytes
    d = n = Integer(1)
    e = Integer(e)
    while n.size_in_bits() != bits and d < (1 << (bits // 2)):
        # Generate prime factors p and q
        size_q = bits // 2
        size_p = bits - size_q
        p = _generateProbablePrime(exact_bits=size_p, randfunc=randfunc)
        q = _generateProbablePrime(exact_bits=size_q, randfunc=randfunc)
        n = p * q
        lcm = (p - 1).lcm(q - 1)
        d = e.inverse(lcm)
    if p > q:
        p, q = q, p
    u = p.inverse(q)
    logger.debug(f"Generated RSA key with n: {n}, e: {e}, d: {d}, p: {p}, q: {q}, u: {u}")
    return RSA.RsaKey(n=n, e=e, d=d, p=p, q=q, u=u)
def generateSigningKeys(size=DEFAULT_RSA_KEY_SIZE, publicExponent=DEFAULT_RSA_PUBLIC_EXPONENT):
    logger.info(f"Generating RSA signing keys with size={size} bits and publicExponent={publicExponent}.")
    key = _generateRSAKey(size, e=publicExponent)
    private_key_pem = key.export_key('PEM')
    public_key_pem = key.publickey().export_key('PEM')
    logger.debug("RSA signing keys generated successfully.")
    return private_key_pem, public_key_pem
def generateEncryptingKey(size=DEFAULT_AES_KEY_SIZE):
    if size not in _AES_POSSIBLE_SIZES:
        logger.error(f"Invalid AES key size: {size}. Allowed sizes: {_AES_POSSIBLE_SIZES}.")
        raise ValueError('Only possible key sizes are %s.' % ', '.join(map(str, _AES_POSSIBLE_SIZES)))
    encrypting_key = Random.get_random_bytes(size // 8)
    logger.info(f"Generated AES encrypting key of size {size} bits.")
    return encrypting_key
def createProductKey(signingPrivateKey, licenseId, productId, expirationTime, groupsLength,
                    licenseIdSize, productIdSize, expirationTimeSize, hashSize):
    logger.info(f"Creating shortened product key with License ID: {licenseId}, Product ID: {productId}, Expiration Time: {expirationTime}.")
    if licenseId >= 2 ** licenseIdSize:
        logger.error('License ID is too big for its field size.')
        raise ValueError('License ID is too big for its field size.')
    if productId >= 2 ** productIdSize:
        logger.error('Product ID is too big for its field size.')
        raise ValueError('Product ID is too big for its field size.')
    if expirationTime >= 2 ** expirationTimeSize:
        logger.error('Expiration time is too big for its field size.')
        raise ValueError('Expiration time is too big for its field size.')
    licenseId_bits = format(licenseId, f'0{licenseIdSize}b')
    productId_bits = format(productId, f'0{productIdSize}b')
    expirationTime_bits = format(expirationTime, f'0{expirationTimeSize}b')
    combined_bits = licenseId_bits + productId_bits + expirationTime_bits
    logger.debug(f"Combined bits before hashing: {combined_bits}")
    hash_obj = SHA256.new(long_to_bytes(int(combined_bits, 2)))
    logger.debug(f"SHA256 Hash of combined bits: {hash_obj.hexdigest()}")
    try:
        private_key = RSA.import_key(signingPrivateKey)
        signature = pkcs1_15.new(private_key).sign(hash_obj)
        logger.debug(f"Generated signature: {signature.hex()}")
    except (ValueError, TypeError) as e:
        logger.exception("Failed to sign the product key data.")
        raise
    signature_bits = ''.join(f'{byte:08b}' for byte in signature)[:hashSize * 8]
    final_bits = combined_bits + signature_bits
    logger.debug(f"Final bits with signature: {final_bits}")
    final_int = int(final_bits, 2)
    logger.debug(f"Final integer before encoding: {final_int}")
    final_bytes = long_to_bytes(final_int)
    product_key_b32 = b32encode(final_bytes).decode('utf-8').strip('=')
    logger.debug(f"Product key in Base32: {product_key_b32}")
    if groupsLength:
        groups = textwrap.wrap(product_key_b32, groupsLength)
        formatted_product_key = '-'.join(groups[:4])
    else:
        formatted_product_key = product_key_b32
    logger.debug(f"Formatted product key: {formatted_product_key}")
    return formatted_product_key#utils.py 
import time
from base64 import b32decode
from Cryptodome.Util.number import bytes_to_long
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHAKE256
import logging
logger = logging.getLogger(__name__)
class InvalidProductKey(Exception):
    def __init__(self):
        super().__init__('Invalid product key.')
def readProductKey(verifyingPublicKeyInstance, licenseIdSize, productIdSize, expirationTimeSize, hashSize, productKey):
    try:
        productKey = productKey.strip().replace('-', '').replace(' ', '')
        productKeyBytes = b32decode((productKey + '=' * (-len(productKey) % 8)).encode('utf-8'))
        productKeyLong = bytes_to_long(productKeyBytes)
        longValue = verifyingPublicKeyInstance._encrypt(productKeyLong)
        bitString = ('{:0' + str(licenseIdSize + productIdSize + expirationTimeSize + hashSize * 8) + 'b}').format(
            longValue)
        licenseId = int(bitString[:licenseIdSize], 2)
        productId = int(bitString[licenseIdSize:licenseIdSize + productIdSize], 2)
        expirationTime = int(
            bitString[licenseIdSize + productIdSize:licenseIdSize + productIdSize + expirationTimeSize], 2)
        computedHash = int(bitString[licenseIdSize + productIdSize + expirationTimeSize:], 2)
        shake = SHAKE256.new()
        shake.update(bitString[:licenseIdSize + productIdSize + expirationTimeSize].encode('utf-8'))
        if computedHash != bytes_to_long(shake.read(hashSize)):
            raise InvalidProductKey()
        return licenseId, productId, expirationTime
    except:
        raise InvalidProductKey()# protector.py 
import base64
import time
import logging
import string

import os
from Cryptodome.Cipher import AES, PKCS1_OAEP
from Cryptodome.PublicKey import RSA
from Cryptodome.Util.Padding import unpad, pad
from Cryptodome.Protocol.KDF import PBKDF2
from Cryptodome.Hash import SHA256
from Cryptodome.Random import get_random_bytes
from getpass import getpass
from LicenseMaker import generateEncryptingKey, encrypt_file
from runtime import validate  # Ensure runtime.py is in the same directory or PYTHONPATH
from LicenseMaker import configure
import json  # Added for JSON serialization
def setup_logging():
    logger = logging.getLogger('Protecter')
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    file_handler = logging.FileHandler('protecter.log')
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(formatter)
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)
    return logger
logger = setup_logging()
def encrypt_private_key(private_key_pem, passphrase):
    logger.info("Encrypting the RSA private key with passphrase.")
    try:
        salt = get_random_bytes(16)
        logger.debug(f"Generated salt: {base64.b64encode(salt).decode('utf-8')}")
        key = PBKDF2(passphrase, salt, dkLen=32, count=100000, hmac_hash_module=SHA256)
        logger.debug(f"Derived key: {base64.b64encode(key).decode('utf-8')}")
        cipher = AES.new(key, AES.MODE_GCM)
        ciphertext, tag = cipher.encrypt_and_digest(private_key_pem)
        encrypted_key = {
            'ciphertext': base64.b64encode(ciphertext).decode('utf-8'),
            'tag': base64.b64encode(tag).decode('utf-8'),
            'nonce': base64.b64encode(cipher.nonce).decode('utf-8'),
            'salt': base64.b64encode(salt).decode('utf-8')
        }
        encrypted_key_json = json.dumps(encrypted_key)
        encrypted_key_b64 = base64.b64encode(encrypted_key_json.encode('utf-8')).decode('utf-8')
        logger.info("RSA private key encrypted successfully with passphrase.")
        return encrypted_key_b64
    except Exception as e:
        logger.exception("Failed to encrypt the RSA private key with passphrase.")
        raise e
def create_protected_script(encrypted_content_b64, iv_b64, output_file):
    logger.info(f"Creating protected script: {output_file}")
    try:
        template = '''
import base64
import time
import logging
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
from runtime import validate
from LicenseMaker import configure
def setup_logging():
    logger = logging.getLogger('ProtectedScript')
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    file_handler = logging.FileHandler('protected_script.log')
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(formatter)
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)
    return logger
logger = setup_logging()
def decrypt_content(encrypted_content_b64, iv_b64, encrypting_key_b64):
    try:
        logger.info("Starting decryption process.")
        encrypted_content = base64.b64decode(encrypted_content_b64)
        iv = base64.b64decode(iv_b64)
        encrypting_key = base64.b64decode(encrypting_key_b64)
        cipher = AES.new(encrypting_key, AES.MODE_CBC, iv=iv)
        decrypted_padded = cipher.decrypt(encrypted_content)
        decrypted_content = unpad(decrypted_padded, AES.block_size)
        logger.info("Decryption successful.")
        return decrypted_content
    except (ValueError, KeyError) as e:
        logger.exception("Decryption failed. Invalid key or corrupted data.")
        raise
def main():
    license_key = input("Enter your license key: ").strip()
    logger.info("License key entered.")
    try:
        with open('public_key.pem', 'rb') as f:
            public_key = f.read()
        with open('encrypting_key.b64', 'r') as f:
            encrypting_key_b64 = f.read().strip()
        configure(
            productKey=license_key,
            verifyingPublicKey=public_key,
            encryptingKey=base64.b64decode(encrypting_key_b64),
            expectedProductIds=10,
            licenseIdSize=24,
            productIdSize=16,
            expirationTimeSize=40,
            hashSize=6
        )
        logger.info("Runtime configured successfully.")
    except Exception as e:
        logger.exception("Failed to configure runtime.")
        print(f"Runtime configuration failed: {str(e)}")
        return
    try:
        license_info = validate()
        logger.info(f"License is valid. Expires on {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(license_info['expirationTime']))}")
    except Exception as e:
        logger.error("License validation failed.")
        print(str(e))
        return
    try:
        encrypted_content_b64 = "$encrypted_content_b64"
        iv_b64 = "$iv_b64"
        decrypted_content = decrypt_content(encrypted_content_b64, iv_b64, encrypting_key_b64)
        exec(decrypted_content, globals())
    except Exception as e:
        logger.exception("Failed to decrypt or execute the original script.")
        print(f"Failed to execute the script: {str(e)}")
if __name__ == "__main__":
    main()
        '''
        tmpl = string.Template(template)
        filled_template = tmpl.safe_substitute(
            encrypted_content_b64=encrypted_content_b64,
            iv_b64=iv_b64
        )
        with open(output_file, 'w') as f:
            f.write(filled_template)
        logger.info(f"Protected script '{output_file}' created successfully.")
    except Exception as e:
        logger.exception("Failed to create the protected script.")
        raise eimport tkinter as tk 
from tkinter import filedialog, messagebox, ttk
import os
import base64
import time
import logging
from LicenseMaker import (
    generateSigningKeys,
    generateEncryptingKey,
    createProductKey,
    configureLicenseComputation,
    encrypt_file
)
from runtime import (
    InvalidProductKey,
    InvalidProductId,
    ExpiredLicense
)
from Cryptodome.PublicKey import RSA
from protector import create_protected_script, encrypt_private_key, setup_logging as protecter_setup_logging
import json  # Added for JSON serialization
def setup_logging():
    logger = logging.getLogger('LicenseMakerGUI')
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    file_handler = logging.FileHandler('app.log')
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(formatter)
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)
    return logger
logger = setup_logging()
class LicenseMakerGUI:
    def __init__(self, master):
        self.master = master
        master.title("License Maker GUI")
        master.geometry("700x700")
        master.resizable(False, False)
        master.configure(bg='#000000')
        self.selected_file = None
        self.license_duration = tk.StringVar(value="1 Month")
        self.generated_private_key = None
        self.generated_public_key = None
        self.generated_encrypting_key = None
        self.generated_product_key = None
        self.default_passphrase = "YourSecurePassphrase123!"
        self.selected_file = None
        self.create_widgets()
    def create_widgets(self):
        padding = {'padx': 10, 'pady': 10}
        label_fg = "#FFFFFF"
        entry_bg = "#1E1E1E"
        entry_fg = "#FFFFFF"
        button_bg = "#4CAF50"
        button_fg = "#FFFFFF"
        file_frame = tk.LabelFrame(self.master, text="Select Python File", bg='#000000', fg=label_fg)
        file_frame.pack(fill="x", **padding)
        self.file_entry = tk.Entry(file_frame, width=50, bg=entry_bg, fg=entry_fg, insertbackground='white')
        self.file_entry.pack(side="left", fill="x", expand=True, padx=(10, 0), pady=10)
        browse_button = tk.Button(file_frame, text="Browse", command=self.browse_file, bg=button_bg, fg=button_fg)
        browse_button.pack(side="left", padx=10, pady=10)
        duration_frame = tk.LabelFrame(self.master, text="Select License Duration", bg='#000000', fg=label_fg)
        duration_frame.pack(fill="x", **padding)
        duration_label = tk.Label(duration_frame, text="Duration:", bg='#000000', fg=label_fg)
        duration_label.pack(side="left", padx=10, pady=10)
        duration_options = ["1 Day", "1 Week", "1 Month", "1 Year", "Lifetime"]
        self.duration_combobox = ttk.Combobox(duration_frame, textvariable=self.license_duration,
                                              values=duration_options, state="readonly")
        self.duration_combobox.pack(side="left", padx=10, pady=10)
        self.duration_combobox.current(2)
        generate_frame = tk.Frame(self.master, bg='#000000')
        generate_frame.pack(fill="x", **padding)
        generate_button = tk.Button(generate_frame, text="Generate Keys & License", command=self.generate_all,
                                    bg=button_bg, fg=button_fg, font=('Helvetica', 12, 'bold'))
        generate_button.pack(fill="x", padx=50, pady=20)
        save_button = tk.Button(generate_frame, text="Save Keys & License", command=self.save_keys_and_license,
                                bg="#2196F3", fg=button_fg, font=('Helvetica', 12, 'bold'))
        save_button.pack(fill="x", padx=50, pady=10)
        output_frame = tk.LabelFrame(self.master, text="Generated Information", bg='#000000', fg=label_fg)
        output_frame.pack(fill="both", expand=True, **padding)
        self.output_text = tk.Text(output_frame, wrap="word", state="disabled", bg=entry_bg, fg=entry_fg,
                                   insertbackground='white')
        self.output_text.pack(fill="both", expand=True, padx=10, pady=10)
    def browse_file(self):
        filepath = filedialog.askopenfilename(title="Select Python File", filetypes=[("Python Files", "*.py")])
        if filepath:
            self.selected_file = filepath
            self.file_entry.delete(0, tk.END)
            self.file_entry.insert(0, filepath)
            logger.info(f"Selected file: {filepath}")
        else:
            logger.warning("No file selected.")

    def generate_all(self):
        if not self.selected_file:
            messagebox.showerror("Error", "Please select a Python file to protect.")
            logger.error("Generate operation aborted: No file selected.")
            return
        try:
            self.passphrase_value = self.default_passphrase
            logger.info("Generating RSA signing keys.")
            private_key, public_key = generateSigningKeys()
            self.generated_private_key = private_key
            self.generated_public_key = public_key

            logger.info("Generating AES encrypting key.")
            encrypting_key = generateEncryptingKey()
            self.generated_encrypting_key = encrypting_key

            duration = self.license_duration.get()
            logger.info(f"Calculating expiration time for duration: {duration}")
            expiration_time = self.calculate_expiration_time(duration)

            license_id = 1  # You can customize this as needed
            product_id = 10  # You can customize this as needed
            logger.info(
                f"Creating product key with License ID: {license_id}, Product ID: {product_id}, Expiration Time: {expiration_time}")
            product_key = createProductKey(
                signingPrivateKey=private_key,
                licenseId=license_id,
                productId=product_id,
                expirationTime=expiration_time,
                groupsLength=5,
                productIdSize=16,
                expirationTimeSize=40,
                hashSize=6
            )
            self.generated_product_key = product_key
            with open('product_license.key', 'w') as license_file:
                license_file.write(product_key)
            logger.info("Configuring license computation.")
            configuration = configureLicenseComputation(
                verifyingPublicKey=public_key,
                encryptingKey=encrypting_key,
                expectedProductIds=product_id,
                licenseIdSize=24,
                productIdSize=16,
                expirationTimeSize=40,
                hashSize=6
            )

            logger.info("Encrypting the selected Python file and creating the protected script.")
            encrypted_content, iv = encrypt_file(self.selected_file, encrypting_key)
            encrypted_content_b64 = base64.b64encode(encrypted_content).decode('utf-8')
            iv_b64 = base64.b64encode(iv).decode('utf-8')

            encrypted_private_key_b64 = encrypt_private_key(private_key, self.passphrase_value)
            with open('private_key_encrypted.b64', 'w') as key_file:
                key_file.write(encrypted_private_key_b64)
            logger.info("Encrypted RSA private key saved to 'private_key_encrypted.b64'.")

            with open('public_key.pem', 'wb') as pub_key_file:
                pub_key_file.write(public_key)
            logger.info("RSA public key saved to 'public_key.pem'.")

            with open('encrypting_key.b64', 'w') as key_file:
                key_file.write(base64.b64encode(encrypting_key).decode('utf-8'))
            logger.info("AES encrypting key saved to 'encrypting_key.b64'.")

            # Automatically embed the license setup into the selected Python file without overwriting the original code
            protected_script_name = f"protected_{os.path.basename(self.selected_file)}"
            create_protected_script(encrypted_content_b64, iv_b64, output_file=protected_script_name)
            self.embed_license_in_script(protected_script_name)

            self.display_output(public_key, product_key, expiration_time, protected_script_name)
            logger.info("Keys and License generated and protected script created successfully.")
            messagebox.showinfo("Success", "Keys and License generated successfully!\nProtected script created.")
        except Exception as e:
            logger.exception("An error occurred during key and license generation.")
            messagebox.showerror("Error", f"An error occurred: {str(e)}")

    def embed_license_in_script(self, protected_script_name):
        try:
            # Read the protected script with encryption and license code
            with open(protected_script_name, 'r') as file:
                protected_script_content = file.read()

            # Read the original Python file to retain the original code
            with open(self.selected_file, 'r') as original_file:
                original_code = original_file.read()

            # Create a backup of the original file before making changes
            backup_file = self.selected_file + ".bak"
            with open(backup_file, 'w') as backup:
                backup.write(original_code)
            logger.info(f"Backup of original file created: {backup_file}")

            # Write the license and encryption logic at the top, followed by the original code
            with open(self.selected_file, 'w') as original_file:
                # Write the protected license and encryption code at the top
                original_file.write("# --- License and Encryption Setup ---\n")
                original_file.write(protected_script_content)
                original_file.write("\n\n# --- Original Code ---\n")

                # Then write the original code back into the file
                original_file.write(original_code)

            logger.info(f"License setup successfully embedded at the top of the original file: {self.selected_file}")
        except Exception as e:
            logger.exception(f"Failed to embed license into the script: {self.selected_file}")
            raise e

    def calculate_expiration_time(self, duration_str):
        current_time = int(time.time())
        logger.debug(f"Calculating expiration time for duration: {duration_str}")
        if duration_str == "1 Day":
            expiration = current_time + 86400  # 24 * 60 * 60
        elif duration_str == "1 Week":
            expiration = current_time + 604800  # 7 * 86400
        elif duration_str == "1 Month":
            expiration = current_time + 2592000  # 30 * 86400
        elif duration_str == "1 Year":
            expiration = current_time + 31536000  # 365 * 86400
        elif duration_str == "Lifetime":
            expiration = current_time + 10 * 31536000  # 10 Years as Lifetime
        else:
            logger.error(f"Invalid duration selected: {duration_str}")
            raise ValueError("Invalid duration selected.")
        logger.debug(f"Expiration time (Unix Timestamp): {expiration}")
        return expiration
    def display_output(self, public_key, product_key, expiration_time, protected_script_name):
        self.output_text.config(state="normal")
        self.output_text.delete(1.0, tk.END)
        expiration_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expiration_time))
        output = (
            f"Selected File: {self.selected_file}\n"
            f"Protected Script: {protected_script_name}\n\n"
            f"=== RSA Public Key ===\n{public_key.decode('utf-8')}\n\n"
            f"=== AES Encrypting Key ===\n(Base64-encoded in 'encrypting_key.b64')\n\n"
            f"=== Product License Key ===\n{product_key}\n\n"
            f"Expiration Time (Unix Timestamp): {expiration_time}\n"
            f"Expiration Date: {expiration_date}\n"
        )
        self.output_text.insert(tk.END, output)
        self.output_text.config(state="disabled")
        logger.info("Displayed non-sensitive generated information in the GUI.")
    def save_keys_and_license(self):
        try:
            if not all([self.generated_private_key, self.generated_public_key, self.generated_encrypting_key, self.generated_product_key]):
                messagebox.showerror("Error", "Please generate the keys and license before saving.")
                logger.error("Save operation aborted: One or more keys/licenses are not generated.")
                return
            save_dir = filedialog.askdirectory(title="Select Save Directory")
            if not save_dir:
                logger.warning("Save operation aborted: No directory selected.")
                return
            logger.info(f"Saving keys and license to directory: {save_dir}")
            private_key_src = 'private_key_encrypted.b64'
            private_key_dst = os.path.join(save_dir, "private_key_encrypted.b64")
            if os.path.exists(private_key_src):
                os.replace(private_key_src, private_key_dst)
                logger.debug("Encrypted private key saved successfully.")
            else:
                logger.warning("Encrypted private key file not found.")
            public_key_src = 'public_key.pem'
            public_key_dst = os.path.join(save_dir, "public_key.pem")
            if os.path.exists(public_key_src):
                os.replace(public_key_src, public_key_dst)
                logger.debug("Public key saved successfully.")
            else:
                logger.warning("Public key file not found.")
            encrypting_key_src = 'encrypting_key.b64'
            encrypting_key_dst = os.path.join(save_dir, "encrypting_key.b64")
            if os.path.exists(encrypting_key_src):
                os.replace(encrypting_key_src, encrypting_key_dst)
                logger.debug("Encrypting key saved successfully.")
            else:
                logger.warning("Encrypting key file not found.")
            product_license_src = 'product_license.key'
            product_license_dst = os.path.join(save_dir, "product_license.key")
            if os.path.exists(product_license_src):
                os.replace(product_license_src, product_license_dst)
                logger.debug("Product license key saved successfully.")
            else:
                with open(product_license_dst, "w") as f:
                    f.write(self.generated_product_key)
                logger.info("Product license key created and saved to 'product_license.key'.")
            messagebox.showinfo("Success", "Keys and License saved successfully!")
            logger.info("Keys and License saved successfully.")
        except Exception as e:
            logger.exception("Failed to save keys and license.")
            messagebox.showerror("Error", f"Failed to save keys and license: {str(e)}")
if __name__ == "__main__":
    root = tk.Tk()
    gui = LicenseMakerGUI(root)
    logger.info("License Maker GUI started.")
    root.mainloop()
    logger.info("License Maker GUI closed.")i entered the license key but it says product key is not provided (i also need only a serial key) now my idea we could create a unique product key based of the hwid means this :import hashlib 
import psutil
import ctypes
import win32api
import os
import subprocess
from functools import lru_cache
DWORD = ctypes.c_ulong
WORD = ctypes.c_ushort
LPVOID = ctypes.c_void_p
DWORD_PTR = ctypes.POINTER(DWORD)
class SYSTEM_INFO(ctypes.Structure):
    _fields_ = [("dwOemId", DWORD), ("dwPageSize", DWORD), ("lpMinimumApplicationAddress", LPVOID),
                ("lpMaximumApplicationAddress", LPVOID), ("dwActiveProcessorMask", DWORD_PTR),
                ("dwNumberOfProcessors", DWORD), ("dwProcessorType", DWORD),
                ("dwAllocationGranularity", DWORD), ("wProcessorLevel", WORD),
                ("wProcessorRevision", WORD)]
kernel32 = ctypes.windll.kernel32
GetSystemInfo = kernel32.GetSystemInfo
@lru_cache(maxsize=1)
def get_processor_info():
    system_info = SYSTEM_INFO()
    GetSystemInfo(ctypes.byref(system_info))
    return system_info.dwNumberOfProcessors, system_info.wProcessorLevel, system_info.wProcessorRevision, system_info.dwProcessorType
@lru_cache(maxsize=1)
def get_total_memory():
    return psutil.virtual_memory().total
def get_total_hash_hwid():
    physical_memory = get_total_memory()
    num_procs, proc_level, proc_rev, proc_type = get_processor_info()
    mod = (physical_memory / max(proc_rev, 1)) % 1
    mod_to_int = round(mod, 3) * 1000
    calc_hwid = round((proc_level * (proc_rev * physical_memory)) / num_procs, 0)
    return f"HW{int(calc_hwid) ^ int(mod_to_int * proc_type)}ID"
@lru_cache(maxsize=1)
def get_computer_name():
    buf = ctypes.create_string_buffer(256)
    size = ctypes.c_ulong(256)
    ctypes.windll.kernel32.GetComputerNameA(buf, ctypes.byref(size))
    return buf.value.decode('utf-8')
@lru_cache(maxsize=1)
def get_system_drive_serial_key():
    system_drive = os.getenv("SystemDrive", "C:") + "\\"
    return system_drive, win32api.GetVolumeInformation(system_drive)[1]
@lru_cache(maxsize=1)
def get_motherboard_serial():
    return subprocess.check_output("wmic baseboard get serialnumber", shell=True).decode().split("\n")[1].strip()
@lru_cache(maxsize=1)
def get_bios_serial():
    return subprocess.check_output("wmic bios get serialnumber", shell=True).decode().split("\n")[1].strip()
def generate_unique_hwid():
    hwid = get_total_hash_hwid()
    name = get_computer_name()
    mb_serial = get_motherboard_serial()
    bios_serial = get_bios_serial()
    drive, drive_serial = get_system_drive_serial_key()
    return hashlib.sha256(f"{hwid}-{name}-{drive}-{drive_serial}-{mb_serial}-{bios_serial}".encode()).hexdigest()
if __name__ == "__main__":
    print(generate_unique_hwid())  please replace the product key check means when the user starts the selected.py file it will generates a unique hwid and it will be binded than for this pc!