Skip to main content

Python code protection

Project description

stonefish-code-shield

Industry-grade Python code protection.

PyPi Version

For a comprehensive license management solution that is both developer- and user-friendly, check out the Stonefish License Manager.

Quickstart

Protecting Python Packages

To protect a Python project using the Stonefish Code Shield, ensure that it includes a minimal pyproject.toml file. Then simple replace your build system (e.g., setuptools) with stonefish_code_shield in the pyproject.toml file:

[build-system]
# requires = ["setuptools"]
# build-backend = "setuptools.build_meta"
requires = ["stonefish-code-shield"]
build-backend = "stonefish_code_shield.build_meta"

# ...
# additional project metadata as per PEP 621
# <https://peps.python.org/pep-0621/>
# (recommended)
# ...

Done! Your project is now protected with Stonefish Code Shield. Try it out by running

pip install .

or

(pip install build)
python -m build . --wheel

Protecting Standalone Python Scripts

For individual Python files, you can use the scs command-line utility:

scs /path/to/file.py

How Stonefish Protects Your Code

While compiled languages (C, C++, etc.) keep code hidden, Python packages ship their code to all users, who can access it in various ways. However, this changes when the code is built with the Stonefish Code Shield. Here are some examples of what happens:

Accessing Source Files
  • Without Stonefish Code Shield:

    After installation, users will find the package code in, e.g., ~/.local/lib/python3.12/site-packages/numpy. (The path is different for different operation systems.)

  • When building with the Stonefish Code Shield, though, a source code like

    ├── pyproject.toml
    └── stonefish_example
        └── __init__.py
    

    is installed as

    ├── _agg
    │   ├── __init__.dat
    │   ├── __init__.py
    │   └── __pycache__
    │       └── __init__.cpython-311.pyc
    ├── __init__.py
    └── __pycache__
        └── __init__.cpython-311.pyc
    

    All program logic is encrypted within the binary __init__.dat, while the actual source code remains protected.

inspect — Inspect live objects
import stonefish_example
import inspect

print(inspect.getsource(stonefish_example.solve))
Without Stonefish With Stonefish
def solve():
    magic = 42
    return 2 * magic - 1
[...]
OSError: could not get source code
Dill
import stonefish_example
from dill.source import getsource

print(getsource(stonefish_example.solve))
Without Stonefish With Stonefish
def solve():
    magic = 42
    return 2 * magic - 1
[...]
IndexError: list index out of range
IPython's ??
In [1]: import stonefish_example

In [2]: stonefish_example.solve??
Without Stonefish With Stonefish
Signature: stonefish_example.solve()
Docstring: <no docstring>
Source:
def solve():
    magic = 42
    return 2 * magic - 1
File:      ~/path/to/file.py
Type:      function
Signature: stonefish_example.solve(*args, **kwargs)
Docstring: <no docstring>
File:      Dynamically generated function.
           No source code available.
Type:      function
dis — Disassembler for Python bytecode
import stonefish_example
import dis

dis.dis(stonefish_example.solve)
Without Stonefish With Stonefish
1        0 RESUME                0

2        2 LOAD_CONST            1 (42)
         4 STORE_FAST            0 (magic)

3        6 LOAD_CONST            2 (2)
         8 LOAD_FAST             0 (magic)
        10 BINARY_OP             5 (*)
        14 LOAD_CONST            3 (1)
        16 BINARY_OP            10 (-)
        20 RETURN_VALUE
         0 COPY_FREE_VARS        1

3        2 RESUME                0
         4 PUSH_NULL
         6 LOAD_DEREF            2 (f)
         8 LOAD_FAST             0 (args)
        10 BUILD_MAP             0
        12 LOAD_FAST             1 (kwargs)
        14 DICT_MERGE            1
        16 CALL_FUNCTION_EX      1
        18 RETURN_VALUE
xdis
import stonefish_example
import xdis.std as dis

dis.dis(stonefish_example.solve)
Without Stonefish With Stonefish
2:     0 LOAD_CONST           (42)
       2 STORE_FAST           (magic)

3:     4 LOAD_CONST           (2)
       6 LOAD_FAST            (magic)
       8 BINARY_MULTIPLY
      10 LOAD_CONST           (1)
      12 BINARY_SUBTRACT
      14 RETURN_VALUE
3:     0 LOAD_DEREF           (f)
       2 LOAD_FAST            (args)
       4 BUILD_MAP            0
       6 LOAD_FAST            (kwargs)
       8 DICT_MERGE           1
      10 CALL_FUNCTION_EX     (keyword and positional arguments)
      12 RETURN_VALUE
decompyle3, uncompyle6 etc.

Those tools are meant to recreated Python code from .pyc files. Since Stonefish moves the actual code into an encrypted

decompyle3  ~/path/to/__init__.cpython-38.pyc
Without Stonefish With Stonefish
def solve():
    magic = 42
    return 2 * magic - 1
from ._agg import _Qcbq7 as solve

Guidelines

Here are some guidelines to keep in mind while working with Stonefish Code Shield:

  • Stonefish renames class and function names, so relying on the __name__ attribute in your code won't be possible.

  • Stonefish cannot yet handle relative * imports, e.g.,

    from .utils import *
    

    Make all imports explicit:

    from .utils import div_to_mod, Extractor
    
  • Stonefish discards private (underscored) names from the API. If you want your users to use these variables or functions, you'll have to rename them.

  • Users must add *.dat files to their package data, e.g.,

    [tool.setuptools.package-data]
    "*" = ["*.dat"]
    

    in pyproject.toml. This is because the encrypted code must be shipped with the package.

  • Local imports should be relative (from . import x) instead of using absolute imports (import x) if x/ or x.py is an internal folder or directory.

  • Stonefish places all data files in a flat directory structure, so it cannot handle files that are read from two different Python paths.

    ./data.dat
    
    ./a.py
        Path(__file__).parent / "data.dat"
    
    ./b/b.py
        Path(__file__).parent / .. / "data.dat"
    

More info

Contact support@mondaytech.com for more info.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

stonefish_code_shield-0.3.32-py3-none-any.whl (71.7 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page