Metadata-Version: 2.4
Name: ExoCrypt
Version: 1.2.0
Summary: Chunked authenticated encryption for files and Python project bundles
License: MIT
Keywords: encryption,python,AES-GCM,Argon2id,file-protection,CLI
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=42.0.0
Requires-Dist: argon2-cffi>=23.1.0
Dynamic: license-file

# ExoCrypt 1.2.0

ExoCrypt protects files and Python project bundles using **Argon2id + AES-256-GCM**.

## What's new in 1.2.0

- New authenticated chunked format v3 for file encryption/decryption.
- Large files are processed in 1 MiB chunks without loading the whole file into memory.
- Each chunk is authenticated separately; reordered, altered, missing, or appended chunks are rejected.
- Argon2id key derivation (64 MiB memory, 3 passes, parallelism 4).
- Fresh random 16-byte salt and 8-byte nonce prefix for every encryption; a unique per-chunk nonce is derived from the prefix and chunk counter.
- Atomic output files; partial plaintext is removed if authentication fails.
- Still reads legacy v1 (ExoCrypt 1.0.x) and v2 (ExoCrypt 1.1.x) files.
- Project bundles are encrypted using the v3 format.

## Install

Python 3.10+:

```bash
python -m pip install -e .
```

Dependencies: `cryptography>=42.0.0` and `argon2-cffi>=23.1.0`.

## CLI

```bash
exocrypt protect main.py
exocrypt unprotect main.py.exc -o restored.py
exocrypt verify main.py.exc
exocrypt project ./my_project -o my_project.exc
exocrypt unproject my_project.exc -o ./restored_project
exocrypt --version
```

The CLI prompts for passwords instead of asking you to put them in shell history.

## Python API

```python
from exocrypt import encrypt_file, decrypt_file, verify_file

encrypt_file("main.py", "main.py.exc", "a long unique password")
assert verify_file("main.py.exc", "a long unique password")
decrypt_file("main.py.exc", "restored.py", "a long unique password")
```

For in-memory bytes:

```python
from exocrypt import encrypt_bytes, decrypt_bytes

encrypted = encrypt_bytes(b"secret content", "a long unique password")
assert decrypt_bytes(encrypted, "a long unique password") == b"secret content"
```

## Security notes

- No encryption can be guaranteed impossible to decrypt forever.
- AES-256-GCM and Argon2id are established algorithms. ExoCrypt does not depend on keeping its algorithm secret.
- Use a long, unique password. Weak passwords may be guessed offline.
- A different implementation can decrypt files if it implements this documented format and has the correct password.
- If code is decrypted and executed on a device controlled by someone else, the plaintext may be inspected while it runs. Encryption at rest does not make running code permanently unreadable.
- Keep a secure backup of both your original files and the password. There is no password recovery backdoor.
- The chunked file API limits memory usage; the in-memory `encrypt_bytes` / `decrypt_bytes` functions naturally hold the byte payload in memory.
- Project bundling currently builds a ZIP in memory before encrypting it, so very large directory trees can still use more memory during `project` operations.

## Tests

```bash
python -m pip install -e .
python -m unittest discover -s tests -v
```

## License

MIT
