pyconcrete

repository·master·Indexed 20 days ago

https://github.com/falldog/pyconcrete

A tool to protect Python source code by encrypting .py files into .pye files. It provides a custom execution environment, import hooks for in-memory decryption, and a CLI tool called pyecli for compilation. It supports standalone encrypted scripts, integration with Django, and the creation of encrypted Zipapps (.pyz).

Tokens
4.9K
Snippets
20
Records
27
Agent score
71%

What's inside pyconcrete

  1. How pyconcrete integration works in Django

    master

    In the Django example, pyconcrete is used to protect the application source code within a Docker container.

    Key Integration Details:

    • Encryption: The Django source code located in /code/ is encrypted within the Docker image.
    • Entrypoint Requirements: To ensure the encrypted files are handled correctly, specific entrypoint files must remain as .py files and must include import pyconcrete at the very beginning of the file:
      • /code/pye_web/wsgi.py
      • /code/manage.py
    • Production Warning: The example uses Docker image layers to cache the source code. For production deployments, you must ensure that the Docker build process does not inadvertently cache the original, unencrypted .py source code in any image layers.
    # Example of required entrypoint modification
    import pyconcrete
    # ... rest of the file
  2. How pyconcrete protects Python scripts

    master

    pyconcrete protects Python workflows by encrypting .py files into .pye files (which contain encrypted .pyc content).

    Workflow:

    1. Encryption: You use pyecli to compile .py files into .pye files using a passphrase-derived secret key.
    2. Execution: Since .pye files cannot be run by the standard Python interpreter, you must use the pyconcrete executable to launch them. pyconcrete decrypts the file in memory and then hands control to the Python interpreter.
    3. Import Hooking: pyconcrete hooks the Python import mechanism. When a script calls import MODULE, the hook searches for MODULE.pye, decrypts it via the _pyconcrete.pyd binary extension, and executes the decrypted data.
    4. Binary Security: The secret key is embedded within the _pyconcrete.pyd binary (similar to a DLL or SO), making it difficult to extract via simple HEX inspection.
  3. Use pyconcrete as a library (Partial Encryption)

    master

    You can install pyconcrete in mode=lib to use it within a standard Python script. Note: This mode is less secure than the full exe mode.

    Setup:

    1. Install with mode=lib: pip install pyconcrete --config-settings=setup-args="-Dmode=lib" ...
    2. Import pyconcrete in your entry point script.

    Project Layout Example:

    project_root/
    ├── main.py           # Standard Python script that imports pyconcrete
    ├── pyconcrete/       # The pyconcrete library files
    └── src/
        └── module.pye    # Encrypted modules
    # main.py
    import pyconcrete
    import my_encrypted_module  # This will be loaded from src/*.pye
  4. Build and execute encrypted Zipapps (.pyz)

    master

    You can package encrypted .pye modules into a single .pyz archive for easy distribution. pyconcrete can execute these archives directly.

    Build a .pyz archive

    Use pyecli build-zip to package a source directory.

    Arguments:

    • --source (Mandatory): Directory containing .py files.
    • --output (Mandatory): Output path (use .pyz for executable, .zip for library).
    • --main: Entry point in pkg.mod:fn format. Automatically generates a __main__.py.
    • --ext: Custom encrypted extension (default .pye).
    • --ignore-file-list: Patterns to ignore.

    Execute a .pyz archive

    $ pyconcrete app.pyz [args...]

    Import from a .zip archive

    If a zip archive is added to sys.path, pyconcrete's PyeZipImporter hook will automatically find and decrypt .pye modules inside it.

    import sys
    import pyconcrete
    
    sys.path.insert(0, 'libs.zip')
    import mylib  # Loads mylib.pye from inside libs.zip
    # Build a .pyz with a specific entry point
    $ pyecli build-zip --source=./my_project --output=app.pyz --main=pkg.mod:fn
    
    # Execute the resulting archive
    $ pyconcrete app.pyz
  5. Run the Django pyconcrete example via Docker

    master

    To see a working demonstration of pyconcrete integrated with Django, you can use the provided Docker setup. This will build and run a containerized environment containing an encrypted Django application.

    Prerequisites:

    • Docker installed and running.

    Steps:

    1. Execute the provided shell script to build and start the environment:
      ./bin/run-example-django.sh
    2. Once running, access the application at http://127.0.0.1:5151.
    3. Access the Django admin interface at http://127.0.0.1:5151/admin using the following credentials:
      • Username: admin
      • Password: 1234
    $ ./bin/run-example-django.sh
  6. Execute fully encrypted projects

    master

    Once your files are encrypted (e.g., main.pye and src/*.pye), you cannot use the standard python command. You must use the pyconcrete executable.

    Project Layout Example:

    project_root/
    ├── pyconcrete (executable)
    ├── main.pye
    └── src/
        └── module.pye

    Execution:

    $ pyconcrete main.pye
  7. Install pyconcrete via pip

    master

    Because pyconcrete embeds a secret key into its binary during compilation, pre-built packages are not provided. You must build it yourself using pip.

    Important:

    • You must provide a passphrase via --config-settings to generate the secret key.
    • Use --no-cache-dir to ensure pip does not reuse a previously built version with a different passphrase.
    • Requires pip 23.1+ to support the --config-settings flag.
    # Basic installation with a passphrase
    $ pip install pyconcrete \
        --no-cache-dir \
        --config-settings=setup-args="-Dpassphrase=<Your_Passphrase>"
    
    # Installation with multiple options (e.g., setting mode to 'exe' and installing CLI)
    $ pip install pyconcrete \
        --no-cache-dir \
        --config-settings=setup-args="-Dpassphrase=<Your_Passphrase>" \
        --config-settings=setup-args="-Dmode=exe" \
        --config-settings=setup-args="-Dinstall-cli=true"
  8. How PyeZipImporter works for encrypted ZIP archives

    master

    The PyeZipImporter is a PEP-451 compliant finder and loader that allows you to treat encrypted .pye files inside a ZIP archive as standard Python modules.

    Key behaviors:

    • Hybrid Support: It uses a fallback mechanism (zipimport.zipimporter) to allow standard, unencrypted .py or .pyc files to coexist within the same ZIP archive.
    • Thread-Safe Caching: It maintains a directory cache of ZIP contents to speed up lookups and uses a lock to ensure thread safety during cache population.
    • Encrypted Execution: When a module is requested, it locates the .pye file inside the ZIP, decrypts it in memory, and executes the resulting bytecode within the module's dictionary.
    # Conceptual usage
    import pyconcrete
    pyconcrete.install()
    
    # If 'my_app.pyz' contains 'secret.pye'
    import my_app.secret
  9. Configure pyconcrete installation options

    master

    When installing via pip, you can pass several configuration arguments to the Meson build backend using the --config-settings=setup-args="-D<arg>=<value>" syntax:

    ArgumentDefaultDescription
    passphrase(Mandatory)Used to generate the secret key for encryption.
    ext.pyeCustom extension for encrypted files.
    modeexeSet to exe for the standalone executable or lib for the Python module.
    install-clitrueDetermines whether to install the pyecli command-line tool.
  10. How PyeLoader works for standalone .pye files

    master

    The PyeLoader is a custom SourceFileLoader designed to handle .pye files.

    Key behaviors:

    • Extension Support: It automatically registers the .pye extension (or whatever extension is returned by get_ext()) with Python's import machinery.
    • Decryption: When get_code is called, it reads the file, decrypts the buffer using decrypt_buffer, and validates that the Python magic number matches the current runtime.
    • Source Hiding: It overrides get_source to return None for .pye files, preventing the original (encrypted) bytecode from being exposed as source text.
    • Version Validation: It performs a strict check to ensure the encrypted bytecode was compiled for the current Python version to prevent crashes.
    # Conceptual usage via the import system
    import pyconcrete
    pyconcrete.install()
    
    import my_protected_module
    # my_protected_module is loaded, decrypted, and executed in memory
  11. Compile OpenAES

    master

    OpenAES is a portable C library. To compile it, you must compile the source files in ./src and add ./inc to your include paths.

    If OAES_HAVE_ISAAC is defined (which is the default), you must also link the source files in ./src/isaac and add ./src/isaac to your include paths.

    Build options can be modified by editing ./inc/oaes_config.h.

    To build the test programs using CMake:

    # Linux
    cmake .
    make
    
    # Windows (Visual Studio command line)
    cmake . -G "NMake Makefiles"
    nmake
  12. Use the OpenAES library and CLI

    master

    OpenAES provides both a library interface and a command-line application:

    • Library Usage: For programmatic integration, refer to the header file ./inc/oaes_lib.h for the oaes_lib API definitions.
    • CLI Usage: The oaes command-line application can be accessed via the terminal. Use the --help flag to view the manual and available options.
    • Windows Integration: The oaes_setup Windows installer allows you to interact with files via the Windows shell by right-clicking a file in Windows Explorer and selecting an OpenAES menu subcommand.