Compare commits

..

5 Commits

Author SHA1 Message Date
NikitolProject
7c24ae8558 feat: Delete depth with gitea.com in CI
Some checks failed
CI / test (push) Successful in 42s
Release / Build and release (push) Failing after 1m40s
2026-02-25 03:32:28 +03:00
NikitolProject
b9ed446deb feat: Change CI logic
Some checks failed
CI / test (push) Successful in 41s
Release / Build and release (push) Has been cancelled
2026-02-25 03:19:16 +03:00
NikitolProject
96048f31f2 clean: Delete unused .jar file
All checks were successful
CI / test (push) Successful in 40s
2026-02-25 03:12:51 +03:00
NikitolProject
8920e8be24 feat: add CI with gitea workflows
Some checks failed
CI / test (push) Successful in 1m22s
Release / Build aarch64-unknown-linux-musl (push) Has been cancelled
Release / Build x86_64-pc-windows-gnu (push) Has been cancelled
Release / Package decoders (push) Has been cancelled
Release / Create release (push) Has been cancelled
Release / Build x86_64-unknown-linux-musl (push) Has been cancelled
2026-02-25 02:59:34 +03:00
NikitolProject
e0605b2955 docs: Add README files & etc 2026-02-25 02:50:47 +03:00
5 changed files with 558 additions and 0 deletions

25
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,25 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run tests
run: cargo test --all
- name: Build release
run: cargo build --release
- name: Run shell decoder tests
run: bash shell/test_decoder.sh

View File

@@ -0,0 +1,85 @@
name: Release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
jobs:
release:
name: Build and release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross
- name: Build linux-amd64
run: cross build --release --target x86_64-unknown-linux-musl
- name: Build linux-arm64
run: cross build --release --target aarch64-unknown-linux-musl
- name: Build windows-amd64
run: cross build --release --target x86_64-pc-windows-gnu
- name: Collect release artifacts
run: |
mkdir -p release
cp target/x86_64-unknown-linux-musl/release/encrypted_archive release/encrypted_archive-linux-amd64
cp target/aarch64-unknown-linux-musl/release/encrypted_archive release/encrypted_archive-linux-arm64
cp target/x86_64-pc-windows-gnu/release/encrypted_archive.exe release/encrypted_archive-windows-amd64.exe
cp kotlin/ArchiveDecoder.kt release/
cp shell/decode.sh release/
cd release && sha256sum * > SHA256SUMS
- name: Create release via API
env:
TAG: ${{ gitea.ref_name }}
TOKEN: ${{ secrets.GITEA_TOKEN }}
API_URL: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
run: |
BODY=$(cat <<'NOTES'
## encrypted_archive ${TAG}
### Artifacts
| File | Description |
|------|-------------|
| `encrypted_archive-linux-amd64` | Linux x86_64 (static musl) |
| `encrypted_archive-linux-arm64` | Linux aarch64 (static musl) |
| `encrypted_archive-windows-amd64.exe` | Windows x86_64 |
| `ArchiveDecoder.kt` | Kotlin/Android decoder (source) |
| `decode.sh` | POSIX shell decoder (requires OpenSSL) |
| `SHA256SUMS` | Checksums for all files |
NOTES
)
# Create release
RELEASE_ID=$(curl -s -X POST "${API_URL}/releases" \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${TAG}\",
\"name\": \"${TAG}\",
\"body\": $(echo "$BODY" | jq -Rs .),
\"draft\": false,
\"prerelease\": false
}" | jq -r '.id')
echo "Created release ID: ${RELEASE_ID}"
# Upload each artifact
for file in release/*; do
filename=$(basename "$file")
echo "Uploading ${filename}..."
curl -s -X POST "${API_URL}/releases/${RELEASE_ID}/assets?name=${filename}" \
-H "Authorization: token ${TOKEN}" \
-F "attachment=@${file}"
done
echo "Release ${TAG} published with $(ls release/ | wc -l) assets"

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target
*.jar

224
README.md Normal file
View File

@@ -0,0 +1,224 @@
# encrypted_archive
[Русская версия (README_ru.md)](README_ru.md)
Custom encrypted archive format designed to be **unrecognizable** by standard analysis tools (`file`, `binwalk`, `strings`, hex editors).
## Features
- **AES-256-CBC** encryption with per-file random IVs
- **HMAC-SHA-256** authentication (IV || ciphertext)
- **GZIP compression** with smart detection (skips already-compressed formats)
- **XOR-obfuscated headers** — no recognizable magic bytes
- **Encrypted file table** — metadata invisible in hex dumps
- **Decoy padding** — random 644096 bytes between data blocks
- **Three decoders**: Rust (native CLI), Kotlin (JVM/Android), Shell (POSIX)
## Quick Start
```bash
# Build
cargo build --release
# Pack files into an archive
./target/release/encrypted_archive pack file1.txt photo.jpg -o archive.bin
# Inspect metadata (without extracting)
./target/release/encrypted_archive inspect archive.bin
# Extract files
./target/release/encrypted_archive unpack archive.bin -o ./output/
```
## CLI Reference
### `pack` — Create an encrypted archive
```
encrypted_archive pack <FILES>... -o <OUTPUT> [--no-compress <PATTERNS>...]
```
| Argument | Description |
|----------|-------------|
| `<FILES>...` | One or more files to archive |
| `-o, --output` | Output archive path |
| `--no-compress` | Skip compression for matching filenames (suffix or exact match) |
Compression is automatic for most files. Already-compressed formats (`.zip`, `.gz`, `.jpg`, `.png`, `.mp3`, `.mp4`, `.apk`, etc.) are stored without recompression.
```bash
# Pack with selective compression control
encrypted_archive pack app.apk config.json -o bundle.bin --no-compress "app.apk"
```
### `unpack` — Extract files
```
encrypted_archive unpack <ARCHIVE> [-o <DIR>]
```
| Argument | Description |
|----------|-------------|
| `<ARCHIVE>` | Archive file to extract |
| `-o, --output-dir` | Output directory (default: `.`) |
### `inspect` — View metadata
```
encrypted_archive inspect <ARCHIVE>
```
Displays header fields, file count, per-file sizes, compression status, and integrity hashes without extracting content.
## Decoders
The archive can be decoded by three independent implementations. All produce **byte-identical output** from the same archive.
### Rust (native CLI)
The primary implementation. Used via `unpack` subcommand (see above).
### Kotlin (JVM / Android)
Single-file decoder for JVM environments. No external dependencies — uses `javax.crypto` and `java.util.zip` from the standard library.
**Standalone usage:**
```bash
# Compile
kotlinc kotlin/ArchiveDecoder.kt -include-runtime -d ArchiveDecoder.jar
# Decode
java -jar ArchiveDecoder.jar archive.bin ./output/
```
**As a library in an Android project:**
Copy `kotlin/ArchiveDecoder.kt` into your project source tree. All crypto and compression APIs (`javax.crypto.Cipher`, `javax.crypto.Mac`, `java.util.zip.GZIPInputStream`) are available in the Android SDK.
To use as a library, call the decoding logic directly instead of `main()`:
```kotlin
// Example: decode from a file
val archive = File("/path/to/archive.bin")
val outputDir = File("/path/to/output")
decode(archive, outputDir)
// The decode() function handles:
// 1. XOR header de-obfuscation
// 2. TOC decryption
// 3. Per-file AES decryption + HMAC verification
// 4. GZIP decompression (if compressed)
// 5. SHA-256 integrity check
```
No native `.so` required — pure Kotlin/JVM running on ART.
### Shell (POSIX)
Emergency decoder for POSIX systems **with OpenSSL installed**.
```bash
sh shell/decode.sh archive.bin ./output/
```
**Requirements:** `dd`, `openssl`, `sha256sum`, `gunzip`, and either `xxd` or `od`.
> **Note:** This decoder requires `openssl` for AES and HMAC operations. It will **not** work on minimal environments like BusyBox that lack OpenSSL. For constrained environments, use the Rust or Kotlin decoder instead.
## Format Specification
Full binary format specification: **[docs/FORMAT.md](docs/FORMAT.md)**
### Archive Layout (summary)
```
┌──────────────────────────┐ offset 0
│ Header (40 bytes, XOR) │ magic, version, flags, toc_offset, toc_size, toc_iv, file_count
├──────────────────────────┤ offset 40
│ TOC (encrypted AES-CBC) │ file entries: name, sizes, offsets, IV, HMAC, SHA-256
├──────────────────────────┤
│ Data Block 0 │ AES-256-CBC(GZIP(plaintext))
│ Decoy Padding (random) │ 644096 random bytes
├──────────────────────────┤
│ Data Block 1 │
│ Decoy Padding (random) │
├──────────────────────────┤
│ ... │
└──────────────────────────┘
```
### Flags Byte
| Bit | Mask | Feature |
|-----|------|---------|
| 0 | `0x01` | At least one file is GZIP-compressed |
| 1 | `0x02` | TOC is AES-256-CBC encrypted |
| 2 | `0x04` | Header is XOR-obfuscated |
| 3 | `0x08` | Decoy padding between data blocks |
Standard archives with all features: flags = `0x0F`.
## Security Model
**What this provides:**
- Confidentiality — AES-256-CBC encryption per file
- Integrity — HMAC-SHA-256 per file (encrypt-then-MAC)
- Content verification — SHA-256 hash of original plaintext
- Anti-analysis — no recognizable patterns for `file`, `binwalk`, `strings`
**What this does NOT provide:**
- Key management — v1 uses a hardcoded key (v2 will use HKDF-derived subkeys)
- Forward secrecy
- Protection against targeted cryptanalysis (the XOR key is fixed and public)
The obfuscation layer is designed to resist **casual analysis**, not a determined adversary with knowledge of the format.
## Building from Source
```bash
# Debug build
cargo build
# Release build (optimized)
cargo build --release
# Run all tests (unit + integration + golden vectors)
cargo test
```
### Running Cross-Validation Tests
```bash
# Kotlin decoder tests (requires kotlinc + java)
bash kotlin/test_decoder.sh
# Shell decoder tests (requires openssl + sha256sum)
bash shell/test_decoder.sh
```
## Project Structure
```
encrypted_archive/
├── src/
│ ├── main.rs # CLI entry point
│ ├── cli.rs # Clap argument definitions
│ ├── archive.rs # Pack / unpack / inspect
│ ├── format.rs # Binary format serialization
│ ├── crypto.rs # AES-256-CBC, HMAC-SHA-256, SHA-256
│ ├── compression.rs # GZIP + smart format detection
│ └── key.rs # Cryptographic key
├── kotlin/
│ └── ArchiveDecoder.kt # JVM/Android decoder (single file)
├── shell/
│ └── decode.sh # POSIX shell decoder
├── docs/
│ └── FORMAT.md # Binary format specification (normative)
└── tests/
└── golden_vectors.rs # Known-answer tests
```
## License
TBD

222
README_ru.md Normal file
View File

@@ -0,0 +1,222 @@
# encrypted_archive
Собственный формат зашифрованного архива, **неопознаваемый** стандартными инструментами анализа (`file`, `binwalk`, `strings`, hex-редакторы).
## Возможности
- **AES-256-CBC** шифрование с уникальным случайным IV для каждого файла
- **HMAC-SHA-256** аутентификация (IV || шифротекст)
- **GZIP-сжатие** с интеллектуальным определением (пропускает уже сжатые форматы)
- **XOR-обфускация заголовка** — нет узнаваемых magic bytes
- **Зашифрованная таблица файлов** — метаданные невидимы в hex-дампе
- **Обманные вставки (decoy padding)** — случайные 644096 байт между блоками данных
- **Три декодера**: Rust (нативный CLI), Kotlin (JVM/Android), Shell (POSIX)
## Быстрый старт
```bash
# Сборка
cargo build --release
# Запаковать файлы в архив
./target/release/encrypted_archive pack file1.txt photo.jpg -o archive.bin
# Просмотреть метаданные (без распаковки)
./target/release/encrypted_archive inspect archive.bin
# Распаковать файлы
./target/release/encrypted_archive unpack archive.bin -o ./output/
```
## Команды CLI
### `pack` — Создание зашифрованного архива
```
encrypted_archive pack <FILES>... -o <OUTPUT> [--no-compress <PATTERNS>...]
```
| Аргумент | Описание |
|----------|----------|
| `<FILES>...` | Один или несколько файлов для архивации |
| `-o, --output` | Путь к выходному архиву |
| `--no-compress` | Не сжимать файлы, соответствующие шаблону (суффикс или точное имя) |
Сжатие применяется автоматически. Уже сжатые форматы (`.zip`, `.gz`, `.jpg`, `.png`, `.mp3`, `.mp4`, `.apk` и др.) сохраняются без повторной компрессии.
```bash
# Упаковка с управлением сжатием
encrypted_archive pack app.apk config.json -o bundle.bin --no-compress "app.apk"
```
### `unpack` — Распаковка файлов
```
encrypted_archive unpack <ARCHIVE> [-o <DIR>]
```
| Аргумент | Описание |
|----------|----------|
| `<ARCHIVE>` | Файл архива для распаковки |
| `-o, --output-dir` | Директория для извлечения (по умолчанию: `.`) |
### `inspect` — Просмотр метаданных
```
encrypted_archive inspect <ARCHIVE>
```
Отображает поля заголовка, количество файлов, размеры, статус сжатия и хэши целостности — без извлечения содержимого.
## Декодеры
Архив может быть декодирован тремя независимыми реализациями. Все дают **побайтно идентичный** результат из одного и того же архива.
### Rust (нативный CLI)
Основная реализация. Используется через подкоманду `unpack` (см. выше).
### Kotlin (JVM / Android)
Однофайловый декодер для JVM-окружений. Без внешних зависимостей — использует `javax.crypto` и `java.util.zip` из стандартной библиотеки.
**Автономное использование:**
```bash
# Компиляция
kotlinc kotlin/ArchiveDecoder.kt -include-runtime -d ArchiveDecoder.jar
# Декодирование
java -jar ArchiveDecoder.jar archive.bin ./output/
```
**Как библиотека в Android-проекте:**
Скопируйте `kotlin/ArchiveDecoder.kt` в исходники вашего проекта. Все используемые криптографические и компрессионные API (`javax.crypto.Cipher`, `javax.crypto.Mac`, `java.util.zip.GZIPInputStream`) доступны в Android SDK.
Для использования как библиотеки вызывайте логику декодирования напрямую вместо `main()`:
```kotlin
// Пример: декодирование из файла
val archive = File("/path/to/archive.bin")
val outputDir = File("/path/to/output")
decode(archive, outputDir)
// Функция decode() выполняет:
// 1. XOR-деобфускацию заголовка
// 2. Расшифровку таблицы файлов (TOC)
// 3. AES-расшифровку + HMAC-верификацию каждого файла
// 4. GZIP-декомпрессию (если файл сжат)
// 5. Проверку целостности по SHA-256
```
Нативный `.so` не требуется — чистый Kotlin/JVM, работает на ART.
### Shell (POSIX)
Аварийный декодер для POSIX-систем **с установленным OpenSSL**.
```bash
sh shell/decode.sh archive.bin ./output/
```
**Зависимости:** `dd`, `openssl`, `sha256sum`, `gunzip` и `xxd` или `od`.
> **Важно:** Этот декодер требует `openssl` для операций AES и HMAC. Он **не будет работать** в минимальных окружениях типа BusyBox, где OpenSSL отсутствует. Для ограниченных сред используйте Rust- или Kotlin-декодер.
## Спецификация формата
Полная спецификация бинарного формата: **[docs/FORMAT.md](docs/FORMAT.md)**
### Структура архива (обзор)
```
┌──────────────────────────────┐ смещение 0
│ Заголовок (40 байт, XOR) │ magic, версия, флаги, toc_offset, toc_size, toc_iv, кол-во файлов
├──────────────────────────────┤ смещение 40
│ TOC (зашифрован AES-CBC) │ записи файлов: имя, размеры, смещения, IV, HMAC, SHA-256
├──────────────────────────────┤
│ Блок данных 0 │ AES-256-CBC(GZIP(открытый текст))
│ Обманная вставка (случайная)│ 644096 случайных байт
├──────────────────────────────┤
│ Блок данных 1 │
│ Обманная вставка (случайная)│
├──────────────────────────────┤
│ ... │
└──────────────────────────────┘
```
### Байт флагов
| Бит | Маска | Функция |
|-----|-------|---------|
| 0 | `0x01` | Хотя бы один файл GZIP-сжат |
| 1 | `0x02` | TOC зашифрован AES-256-CBC |
| 2 | `0x04` | Заголовок XOR-обфусцирован |
| 3 | `0x08` | Обманные вставки между блоками данных |
Стандартные архивы со всеми функциями: флаги = `0x0F`.
## Модель безопасности
**Что обеспечивается:**
- Конфиденциальность — AES-256-CBC шифрование каждого файла
- Целостность — HMAC-SHA-256 для каждого файла (encrypt-then-MAC)
- Верификация содержимого — SHA-256 хэш оригинального открытого текста
- Защита от анализа — никаких узнаваемых паттернов для `file`, `binwalk`, `strings`
**Что НЕ обеспечивается:**
- Управление ключами — v1 использует зашитый ключ (v2 будет использовать подключи через HKDF)
- Прямая секретность (forward secrecy)
- Защита от целевого криптоанализа (XOR-ключ фиксирован и публичен)
Слой обфускации рассчитан на противодействие **поверхностному анализу**, а не целенаправленному исследователю, знакомому с форматом.
## Сборка из исходников
```bash
# Отладочная сборка
cargo build
# Релизная сборка (оптимизированная)
cargo build --release
# Запуск всех тестов (юнит + интеграция + golden vectors)
cargo test
```
### Кросс-валидационные тесты
```bash
# Тесты Kotlin-декодера (требуется kotlinc + java)
bash kotlin/test_decoder.sh
# Тесты Shell-декодера (требуется openssl + sha256sum)
bash shell/test_decoder.sh
```
## Структура проекта
```
encrypted_archive/
├── src/
│ ├── main.rs # Точка входа CLI
│ ├── cli.rs # Определение аргументов (Clap)
│ ├── archive.rs # Упаковка / распаковка / инспекция
│ ├── format.rs # Сериализация бинарного формата
│ ├── crypto.rs # AES-256-CBC, HMAC-SHA-256, SHA-256
│ ├── compression.rs # GZIP + определение сжатых форматов
│ └── key.rs # Криптографический ключ
├── kotlin/
│ └── ArchiveDecoder.kt # JVM/Android-декодер (один файл)
├── shell/
│ └── decode.sh # POSIX shell-декодер
├── docs/
│ └── FORMAT.md # Спецификация бинарного формата (нормативная)
└── tests/
└── golden_vectors.rs # Тесты с известными ответами
```
## Лицензия
TBD