Metadata-Version: 2.4
Name: pyencode-protector
Version: 0.3.0
Summary: A small cross-platform Python module protector
Author: PyEncode contributors
License: MIT
Project-URL: Homepage, https://github.com/VanThanBK/pyencode
Project-URL: Repository, https://github.com/VanThanBK/pyencode
Project-URL: Issues, https://github.com/VanThanBK/pyencode/issues
Keywords: python,code-protection,obfuscation,encryption
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: cryptography>=42

# PyEncode

PyEncode là công cụ bảo vệ ứng dụng CPython thuần Python, hướng tới Windows và
Linux. Mỗi module được biên dịch thành code object, tuần tự hóa bằng `marshal`,
nén rồi mã hóa xác thực bằng AES-256-GCM. Runtime giải mã và thực thi trong bộ
nhớ; source và bytecode rõ không được ghi ra file tạm.

Phiên bản 0.3 dùng định dạng PYE2:

- Mỗi artifact có tên ngẫu nhiên 128 bit, ví dụ
  `myapp/7b82a40e9aa14e60db7094f2de3fbc33.pye`.
- PYE2 không có header JSON rõ; file chỉ chứa magic, nonce và ciphertext/tag.
- Tên module, entry point và cờ package nằm trong một module index được mã hóa.
- Manifest format 3 được ký Ed25519 và chứa hash SHA-256 của artifact.
- Runtime, launcher, resource và support tree do builder sao chép đều được ký,
  key-bind và kiểm tra lại lúc `install()`.
- Loader chỉ trả một trampoline nhỏ cho `runpy`, không trả code object thật của
  ứng dụng. Khóa module được dẫn xuất theo nhu cầu và không giữ trong finder.

Mục tiêu là tăng đáng kể chi phí phân tích so với `.pyc`, không phải tạo cơ chế
chống reverse tuyệt đối.

## Python được hỗ trợ

PyEncode 0.3 yêu cầu **CPython chuẩn 3.10 trở lên**. CI kiểm thử trực tiếp các
phiên bản 3.10 đến 3.15; các CPython feature release mới hơn được phép cài và
build theo hướng forward-compatible, rồi được đưa vào ma trận bắt buộc sau khi
có bản phát hành và dependency wheel tương ứng. PyPy và các interpreter khác
hiện chưa được hỗ trợ.

Runtime là Python thuần nên cùng source runtime dùng được trên Windows và
Linux. Tuy nhiên artifact đã build gắn với đúng CPython major/minor:

- build bằng CPython 3.11 → chạy bằng CPython 3.11;
- không chạy artifact đó bằng 3.10, 3.12 hoặc 3.14;
- muốn phát hành cho nhiều minor version, build một output bằng từng interpreter.

Nếu bundle có thêm `.pyd`, `.so`, `.dll` hoặc dependency native qua `--support`,
toàn bundle còn phụ thuộc hệ điều hành, kiến trúc và ABI của các file đó. Phạm
vi kiểm thử hiện tại là CPython GIL tiêu chuẩn; free-threaded/debug ABI cần build
và kiểm tra riêng.

## Cài đặt

Sau khi package được phát hành trên PyPI, cài bằng:

```powershell
py -3.11 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install pyencode-protector
```

Tên lệnh CLI và import namespace vẫn là `pyencode`.

Để phát triển trực tiếp từ source, cài editable trong virtual environment:

Windows PowerShell:

```powershell
py -3.11 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e .
```

Linux:

```bash
python3.11 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .
```

Dependency runtime duy nhất là `cryptography>=42`.

## Sử dụng

Bảo vệ một file:

```bash
pyencode build hello.py -o dist/hello
python dist/hello/run.py
```

Bảo vệ package có `__main__.py`:

```bash
pyencode build src/myapp -o dist/myapp
python dist/myapp/run.py
```

Chỉ định entry module:

```bash
pyencode build src -o dist/app --entry myapp.__main__
```

Ký và key-bind thêm dependency/support tree:

```bash
pyencode build src -o dist/app \
  --entry myapp.main \
  --support build/vendor _vendor \
  --support public/config.json config.json
```

`--support SOURCE DEST` có thể lặp lại. Nếu `SOURCE` là thư mục, nội dung cây
được chép dưới `DEST`; nếu là file, `DEST` là đường dẫn file đầu ra. Symlink,
reparse point, đường dẫn thoát root và va chạm chữ hoa/thường bị từ chối.

Đích chính xác `_vendor` được xem là bootstrap dependency path cho host không
có `cryptography` cài sẵn (như Fusion). Chỉ đúng entry `_vendor` trong
`sys.path` được giữ lúc verifier nạp; mọi đường dẫn con khác trong bundle bị
cách ly. Vì thư viện mật mã vendored phải chạy trước khi Ed25519 có thể tự kiểm
chứng bundle, `_vendor` là một phần bootstrap được tin cậy theo thiết kế của
runtime Python thuần; chỉ đóng gói wheel lấy từ nguồn tin cậy.

Các tùy chọn chính:

- `-o`, `--output`: thư mục output; phải chưa tồn tại hoặc hoàn toàn rỗng.
- `-e`, `--entry`: dotted name của entry module/package.
- `--exclude GLOB`: loại đường dẫn khớp glob; có thể lặp.
- `--no-resources`: không sao chép file không phải Python trong source tree.
- `--keep-docstrings`: giữ docstring.
- `--optimize {0,1,2}`: mức tối ưu CPython.
- `--expires YYYY-MM-DD`: từ chối chạy sau ngày UTC đã cho.
- `--launcher PATH`: dùng launcher `.py` tùy biến.
- `--support SOURCE DEST`: sao chép, ký và key-bind file/cây hỗ trợ.
- `--rename-locals`: đổi tên metadata của local không phải tham số.
- `--allow-extra-data`: cho host tạo thêm file/thư mục dữ liệu không thực thi;
  file đã ký và Python/native artifact lạ vẫn bị kiểm tra.

`--rename-locals` là opt-in vì code dùng alias động của `locals()`, đọc
`frame.f_locals`, debugger hoặc một số framework introspection có thể cần tên
local gốc. Argument, closure, line table và exception table luôn được giữ.

## Launcher tùy biến và host nhúng

Launcher được sao chép nguyên byte, ký và tham gia dẫn xuất khóa. Nó phải đặt:

```python
import sys
sys.dont_write_bytecode = True
```

trước mọi import không phải bootstrap. Builder kiểm tra điều kiện tối thiểu này.
Một launcher ship an toàn cũng phải kiểm tra `pyencode_runtime` không chứa
`__pycache__`, `.pyc` hoặc file lạ **trước** khi import package; launcher mặc
định và pipeline Weldments đã có preflight đầy đủ.

Host như Fusion có thể nạp entry mà không ghi tên module rõ trong stub:

```python
import sys
sys.dont_write_bytecode = True

# Thực hiện preflight pyencode_runtime tại đây.
from pyencode_runtime import load_entry

implementation = load_entry()
```

`install()` cố ý trả `None`. `load_entry()` trả module entry cho embedding host;
`run()` thực thi entry với ngữ nghĩa `__main__`.

## Output

```text
dist/app/
├── .pyencode-manifest.json
├── myapp/
│   ├── 14a0...f91c.pye
│   ├── 7b82...bc33.pye
│   └── assets/config.json
├── pyencode_runtime/
│   ├── __init__.py
│   ├── _build.py
│   ├── _mp_main.py
│   └── _runtime.py
├── requirements.txt
└── run.py
```

Resource không được mã hóa, nhưng byte/path của resource build-time được ký và
key-bind. Mặc định toàn bộ cây output là bất biến: runtime từ chối mọi file hoặc
thư mục được thêm, xóa hay đổi byte.

Với host tự tạo metadata/cache cạnh add-in, `--allow-extra-data` cho phép thêm
file và thư mục dữ liệu mà không làm bundle chết. Chế độ này không cho sửa/xóa
file đã ký, không cho thêm `.py`, `.pyc`, native library hoặc `.pye` lạ, và vẫn
từ chối symlink/reparse point. Không dùng data bổ sung này làm đầu vào tin cậy
cho license hay quyết định bảo mật. Pipeline Weldments bật chế độ tương thích
này để Fusion có thể tạo `.vscode`, log hoặc cache khác nhau giữa các máy.

`__file__` của protected module là đường dẫn thật tới artifact ngẫu nhiên. Vì
vậy `Path(__file__).parent`, `pkgutil.get_data()` và `importlib.resources` kể cả
resource lồng thư mục vẫn hoạt động; code phụ thuộc basename/stem của
`__file__` sẽ thấy token ngẫu nhiên.

Import theo fullname, relative import, circular import, namespace package,
Unicode module, reload, `runpy.run_module()` và `multiprocessing` spawn của app
chạy qua launcher mặc định đều có test tích hợp. `pkgutil.iter_modules()` và
`walk_packages()` không thể tự khám phá tên protected child vì tên đó cố ý nằm
trong index mã hóa; import theo fullname đã biết vẫn hoạt động.

Với embedding host gọi `load_entry()`, spawn hoạt động nếu child cũng chạy lại
bootstrap của host. Trường hợp host chỉ bootstrap bên trong
`if __name__ == '__main__'` rồi spawn trực tiếp một target protected cần launcher
riêng; bootstrap spawn tự động hiện tập trung vào app chạy qua `run()`.

Phiên bản 0.3 dùng một namespace runtime cố định trong mỗi process. Không nạp
hai distribution PyEncode độc lập vào cùng một interpreter; chạy chúng ở hai
process riêng. Runtime namespace ngẫu nhiên/đa-bundle là hạng mục tương thích
trước khi công bố API ổn định 1.0.

## Giới hạn bảo mật

PyEncode làm khó phân tích tĩnh nhưng không thể giữ bí mật tuyệt đối trên máy do
người khác toàn quyền kiểm soát:

- Chế độ mặc định là offline nên key material vẫn phải nằm trong bundle. Người
  có kinh nghiệm có thể đọc runtime và tái tạo quá trình dẫn xuất khóa.
- Code object phải xuất hiện trong bộ nhớ khi thực thi. Tracing, debugger,
  monkeypatch hoặc hook native vẫn có thể quan sát code/runtime state.
- Integrity interlock chặn sửa file, tráo artifact, chèn code và re-sign đơn
  giản; nó không biến Python thuần thành trust anchor native.
- Tên module/entry được che khi nằm yên, nhưng xuất hiện lúc import trong
  `sys.modules`, traceback và runtime state. Thư mục package/resource vẫn có thể
  tiết lộ một phần cấu trúc.
- `--expires` dựa trên đồng hồ hệ thống và không thay thế hệ thống license.

Đừng đặt API key/private key dài hạn trong client. Bước nâng cấp tạo khác biệt
bảo mật thực sự cho sản phẩm giá trị cao là external/envelope key lấy từ license
server hoặc keyring; native runtime chủ yếu tăng thêm chi phí phân tích.

## Kiểm thử

```bash
python -m unittest discover -v
```

CI chạy trên Windows và Ubuntu với CPython 3.10–3.15. Bộ test bao phủ PYE2,
module index mã hóa, KDF vectors, tamper/re-sign, resource/support integrity,
opaque filenames, import/package/namespace/Unicode, `runpy`, multiprocessing
spawn, launcher thường không có `-B`, policy hết hạn và output atomic.

## Phát hành

Quy trình phát hành production bằng GitHub OIDC được ghi tại
[RELEASING.md](RELEASING.md). Workflow chỉ upload lên PyPI khi push tag khớp
chính xác version, ví dụ `v0.3.0`.
