{"id":48980608,"url":"https://github.com/efedotof/vaultly","last_synced_at":"2026-04-25T09:04:53.537Z","repository":{"id":351104044,"uuid":"1152627371","full_name":"efedotof/vaultly","owner":"efedotof","description":"Vaultly — облачное хранилище с end‑to‑end шифрованием и нулевым разглашением. Flutter‑клиент + Spring Boot‑бэкенд. Доверяйте математике!!","archived":false,"fork":false,"pushed_at":"2026-04-18T10:07:23.000Z","size":5883,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-18T11:44:10.208Z","etag":null,"topics":["aes-gcm","cloud-storage","cross-platform","cryptography","end-to-end-encryption","file-encryption","flutter","privacy","rsa-oaep","security","self-hosted","shps","spring-boot","zero-knowledge"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/efedotof.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-08T06:52:21.000Z","updated_at":"2026-04-12T21:49:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/efedotof/vaultly","commit_stats":null,"previous_names":["efedotof/vaultly"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/efedotof/vaultly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/efedotof%2Fvaultly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/efedotof%2Fvaultly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/efedotof%2Fvaultly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/efedotof%2Fvaultly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/efedotof","download_url":"https://codeload.github.com/efedotof/vaultly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/efedotof%2Fvaultly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32256221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T04:23:17.126Z","status":"ssl_error","status_checked_at":"2026-04-25T04:21:53.360Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aes-gcm","cloud-storage","cross-platform","cryptography","end-to-end-encryption","file-encryption","flutter","privacy","rsa-oaep","security","self-hosted","shps","spring-boot","zero-knowledge"],"created_at":"2026-04-18T11:13:29.546Z","updated_at":"2026-04-25T09:04:53.523Z","avatar_url":"https://github.com/efedotof.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Vaultly. Конфиденциальность как стандарт.\n\nVaultly — облачное хранилище нового поколения, созданное для тех, кто понимает разницу между простым паролем и сквозным шифрованием.\n\n### 🔐 End-to-End Encryption (E2EE)\nВаши файлы шифруются на устройстве до отправки в сеть. Сервер видит только зашифрованные блоки данных, не имея ключей для их расшифровки. Это не функция, это фундамент архитектуры Vaultly.\n\n### 📱 Бесшовный доступ\nFlutter-основа обеспечивает плавный и нативный опыт на всех платформах. Получите доступ к своему хранилищу так же легко, как к локальной папке, но с уровнем защиты банковского хранилища.\n\n### 🛡 Целостность данных\nНикакой слежки, никакого анализа содержимого для рекламы. Только вы решаете, что, где и как хранить.\n\n---\n\nVaultly — это ответ тем, кто устал читать новости об утечках. **Доверяйте математике, а не обещаниям.**\n\n---\n\n## 🏛 Архитектура бэкенда\n\nСерверная часть Vaultly построена на **Spring Boot** с применением **PostgreSQL** и **AWS S3**-совместимого объектного хранилища. Вся бизнес-логика спроектирована вокруг принципа **нулевого разглашения (Zero-Knowledge)** — сервер никогда не обрабатывает пользовательские данные в открытом виде.\n\n| Компонент | Технологии | Назначение |\n| :--- | :--- | :--- |\n| **REST API** | Spring Boot, Spring Security | Аутентификация, управление файлами и папками, сессии |\n| **Хранение файлов** | S3 API (Beget Cloud) | Хранение зашифрованных `.shps` контейнеров |\n| **База данных** | PostgreSQL | Метаданные, пользователи, права доступа, ключи устройств |\n| **Криптография** | `javax.crypto`, `java.security` | Реализация протокола SHPS, управление ключами |\n\n---\n\n## 🛡 Протокол шифрования SHPS (Shirmps Protocol)\n\nСердце безопасности Vaultly — собственный формат файлов `.shps`, реализующий гибридное шифрование. Каждый файл, покидающий устройство клиента, упаковывается в криптографический контейнер, который сервер может лишь хранить, но не читать.\n\n### 📦 Структура SHPS-контейнера\n\nФайл `.shps` состоит из трёх логических частей:\n\n1. **Заголовок (Header)** — 4 байта длины + JSON с метаданными и зашифрованным сессионным ключом.\n2. **Зашифрованное тело** — данные, зашифрованные **AES-256-GCM**.\n3. **Тег аутентификации** — встроен в поток GCM.\n\n```java\n// Ключевые поля заголовка (ShirmpsHeader)\nprivate String version = \"1.0\";\nprivate String algorithm = \"AES-256-GCM\";\nprivate String keyEncryption = \"RSA-OAEP\";\nprivate String encryptedKey;       // AES-ключ, зашифрованный RSA\nprivate String iv;                 // Вектор инициализации для AES-GCM\nprivate String signature;          // Цифровая подпись (SHA256withRSA)\nprivate String keyOwner;           // \"user\" или \"server\"\nprivate Map\u003cString, String\u003e metadata; // Сжатие GZIP, уровень компрессии\n```\n\n\u003cimg width=\"3354\" height=\"570\" alt=\"dia\" src=\"https://github.com/user-attachments/assets/1ce657f8-f3e4-41ff-9176-99fe2832a22e\" /\u003e\n\n### ⚙️ Процесс шифрования (гибридная схема)\n\n1. **Генерация сессионного ключа**: Случайный 256-битный ключ AES.\n2. **Сжатие (опционально)**: GZIP с максимальной компрессией для уменьшения трафика.\n3. **Шифрование данных**: `AES/GCM/NoPadding` с вектором инициализации 12 байт. GCM обеспечивает аутентификацию и целостность.\n4. **Шифрование ключа**: Сессионный AES-ключ зашифровывается с помощью `RSA-2048 OAEP (SHA-256)` публичным ключом получателя (пользователя или сервера).\n5. **Подпись**: Хэш SHA-256 от исходных данных подписывается приватным ключом отправителя для гарантии авторства и неизменности.\n\n### 🔓 Режимы работы ключей\n\n| Режим | `keyOwner` | Сценарий использования |\n| :--- | :--- | :--- |\n| **Приватный файл** | `\"user\"` | AES-ключ зашифрован публичным ключом пользователя. Только владелец может расшифровать. Сервер хранит непрозрачный блоб. |\n| **Публичный доступ** | `\"server\"` | AES-ключ зашифрован публичным ключом сервера. Сервер расшифровывает файл для отдачи по временной ссылке. |\n\n### 💾 Потоковая обработка\n\nБэкенд реализует потоковое шифрование/дешифрование с минимальным потреблением памяти, используя `CipherInputStream` / `CipherOutputStream`. Это позволяет обрабатывать файлы размером в десятки гигабайт, не загружая их целиком в оперативную память.\n\n```java\n// Пример потоковой расшифровки для публичной ссылки (ShpsSecurityService)\npublic void decryptServerEncryptedToStream(InputStream shpsStream, OutputStream out) {\n    // 1. Чтение заголовка\n    // 2. Расшифровка AES-ключа серверным приватным ключом\n    // 3. CipherInputStream -\u003e (GZIPInputStream) -\u003e OutputStream\n}\n```\n### 🗄 Модель данных и безопасность ключей\n\nБаза данных спроектирована так, чтобы даже в случае компрометации SQL-дампа злоумышленник не получил доступ к содержимому файлов.\n\n- **Пользователи (`users`)**: Хранят публичный ключ RSA, зашифрованный приватный ключ (защищён паролем пользователя с использованием `salt`), `storage_used`, `storage_limit`.\n- **Устройства (`devices`)**: Каждое клиентское устройство (смартфон, ПК) регистрирует свой уникальный ID и публичный ключ. Приватный ключ пользователя хранится на устройстве в зашифрованном виде и синхронизируется через бэкенд (зашифрован публичным ключом устройства).\n- **Файлы (`files`)**: Хранят `s3_key` на объект `.shps`. Поле `is_public` управляет режимом шифрования (`keyOwner`).\n- **Временные ссылки (`temp_file_access`)**: Токены для публичного доступа с ограничением по времени и количеству скачиваний. Пароль на ссылку хранится в виде хэша.\n\n### 🔑 Управление ключами сервера\n\nПара ключей сервера загружается из файлов `.pem`, пути к которым задаются в `.env`. Для шифрования сессионных ключей используется строго `OAEPWithSHA-256AndMGF1Padding`. При старте приложения сервер проверяет валидность пары ключей тестовым шифрованием/дешифрованием.\n\n```java\n// ServerKeyService: загрузка ключей и проверка пары\nCipher cipher = Cipher.getInstance(\"RSA/ECB/OAEPWithSHA-256AndMGF1Padding\");\ncipher.init(Cipher.ENCRYPT_MODE, serverPublicKey);\nbyte[] encrypted = cipher.doFinal(testData);\ncipher.init(Cipher.DECRYPT_MODE, serverPrivateKey);\nbyte[] decrypted = cipher.doFinal(encrypted);\n```\n\n---\n### 📱 Клиентская часть (Flutter)\n---\n\nКлиент, написанный на Flutter, выполняет всю криптографическую работу локально, не доверяя серверу:\n\n- Генерация и управление ключевыми парами RSA.\n- Шифрование файлов перед отправкой.\n- Дешифрование на лету при скачивании.\n- Бесшовная синхронизация состояния с бэкендом через REST API.\n\n### 🚀 Быстрый старт (Backend)\n\n1. **Клонируйте репозиторий**\n   ```bash\n   git clone https://github.com/your-org/vaultly-backend.git\n   cd vaultly-backend\n   ```\n\n2. **Настройте переменные окружения (файл `.env`)**\n   ```dotenv\n   SERVER_PRIVATE_KEY_PATH=/path/to/private.pem\n   SERVER_PUBLIC_KEY_PATH=/path/to/public.pem\n   S3_BUCKET=your-bucket-name\n   S3_PUBLIC_URL=https://s3.example.com/bucket-name\n   ```\n3. **Запустите PostgreSQL и выполните миграции**\n   Миграции Flyway (`V1__initial_schema.sql` и последующие) создадут все необходимые таблицы и триггеры.\n\n4. **Соберите и запустите**\n   ```bash\n   ./mvnw spring-boot:run\n   ```\n\n\n## 📱 Клиентская часть (Flutter)\n\nКлиент Vaultly написан на Flutter и обеспечивает полную реализацию end‑to‑end шифрования непосредственно на устройстве пользователя. Приложение работает на Android, iOS, Windows, macOS, Linux и в браузере (Web), используя единую кодовую базу.\n\n### 🧩 Ключевые зависимости\n\n- **Управление состоянием и маршрутизация:** `flutter_bloc`, `auto_route`  \n- **Сетевое взаимодействие:** `dio`, `http`  \n- **Локальное хранение:** `flutter_secure_storage`, `shared_preferences`, `path_provider`  \n- **Криптография:**  \n  - Для нативных платформ – `pointycastle`, `encrypt`, `basic_utils`  \n  - Для Web – `webcrypto` (реализация Web Crypto API)  \n- **Работа с файлами:** `file_picker`, `open_filex`, `flutter_pdfview`, `media_kit`  \n- **Дополнительно:** `device_info_plus` (генерация идентификатора устройства), `permission_handler`, `qr_flutter`\n\n### 🔐 Криптографическая подсистема\n\nВся криптография выполняется локально. Приложение никогда не передаёт на сервер незашифрованные данные или приватные ключи.\n\n#### Генерация и управление ключами\n\n- **`RsaKeyGeneratorWeb`** – генерация 2048‑битных пар RSA (для Web и нативных платформ).  \n- **`KeyManagerService` / `KeyManagerServiceWeb`** – централизованное управление ключами пользователя и устройства:\n  - Хранение публичных ключей в открытом виде.  \n  - Приватные ключи шифруются паролем пользователя (AES‑GCM, ключ выводится через PBKDF2 с солью).  \n  - Кэширование расшифрованного приватного ключа в памяти на время сессии.  \n  - Отдельные пары ключей для пользователя (для шифрования файлов) и для текущего устройства (для синхронизации зашифрованного пользовательского приватного ключа через сервер).  \n- **`SecureStorageAdapter`** – адаптер поверх `flutter_secure_storage` и `shared_preferences`:\n  - На iOS/Android/macOS/Windows/Linux использует защищённое хранилище (Keychain/Keystore).  \n  - В Web данные сохраняются в `SharedPreferences` (с учётом ограничений платформы).  \n- **`AuthLocalStorage`** – сохранение токенов доступа, информации о пользователе и пароля, зашифрованного мастер‑ключом (AES‑GCM).\n\n#### Формат SHPS\n\n- **`ShirmpsHeader`** – структура заголовка контейнера `.shps` с метаданными, зашифрованным AES‑ключом, IV и цифровой подписью.  \n- **`ShirmEncryptionService`** (нативный) и **`ShirmEncryptionServiceWeb`** (Web):\n  - Генерация случайного AES‑256 ключа и 12‑байтного IV.  \n  - Шифрование данных алгоритмом AES‑GCM (аутентифицированное шифрование).  \n  - Зашифрование AES‑ключа публичным RSA‑ключом получателя (OAEP с SHA‑256).  \n  - Опциональная цифровая подпись исходных данных приватным ключом отправителя (SHA256withRSA).  \n  - Формирование итогового `.shps` контейнера: `[4 байта длины заголовка][JSON‑заголовок][зашифрованные данные]`.  \n- **`ShirmDecryptionService`** и **`ShirmDecryptionServiceWeb`** – обратный процесс с расшифровкой AES‑ключа и данных.\n\n#### Обработка публичных файлов\n\n- **`PublicFileDecryptionService`** – специализированная расшифровка файлов, предназначенных для публичного доступа (когда сервер перешифровывает AES‑ключ на публичный ключ запросившего пользователя).\n\n### 🗂 Локальное кэширование файлов\n\n- **`LocalFileCache`** – сохраняет расшифрованные файлы в директории приложения (`file_cache`) для быстрого повторного доступа без повторной загрузки и расшифровки.  \n- Для каждого файла сохраняется его оригинальное имя в отдельном файле `.name`.  \n- Поддерживается очистка кэша и расчёт занимаемого объёма.  \n- На Web кэширование отключено (ограничения платформы).\n\n### 🔄 Расшифровка файлов «на лету»\n\n- **`FileDecryptionService`** – оркеструет процесс получения файла:\n  1. Проверка наличия в локальном кэше.  \n  2. Загрузка `.shps` контейнера с сервера (через `FileInterface`).  \n  3. Определение типа файла (приватный или публичный).  \n  4. Расшифровка с использованием приватного ключа пользователя (запрашивается пароль).  \n  5. Сохранение расшифрованных данных в кэш.  \n\n### 🌐 Уникальная идентификация устройства\n\n- **`DeviceIdGenerator`** – генерирует стабильный идентификатор устройства на основе аппаратных и системных характеристик (Android ID, identifierForVendor на iOS, machine ID на Linux и т.д.) с хэшированием SHA‑256. Используется для регистрации устройства на сервере и управления ключами синхронизации.\n\n### 🧪 Поддержка Web\n\nДля работы в браузере реализованы отдельные сервисы, использующие `package:webcrypto` (Web Crypto API) вместо `pointycastle`. Это обеспечивает высокую производительность криптографических операций и соответствие стандартам безопасности браузеров.\n\n### 🔒 Принципы безопасности клиента\n\n- Приватные ключи **никогда не покидают устройство в открытом виде**.  \n- Все криптографические операции выполняются локально.  \n- Пароль пользователя используется только для расшифровки локального приватного ключа; сервер его не получает.  \n- Кэшированные файлы хранятся в открытом виде, но доступ к ним ограничен файловой системой приложения.  \n- Идентификатор устройства не содержит персональных данных, только хэш системных атрибутов.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fefedotof%2Fvaultly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fefedotof%2Fvaultly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fefedotof%2Fvaultly/lists"}