ZEC (Zipped Encrypted Container) — это контейнерная система для безопасного хранения секретов в зашифрованном виде. Все секреты хранятся в файлах с расширением *.zec.
Идея заключается в хранении секретов в сжатом и зашифрованном виде. При чтении файла загружаются только заголовок и индексная таблица, что позволяет узнать содержимое контейнера без чтения самих секретов.
Ключевые особенности:
- Безопасность: Все данные зашифрованы алгоритмом ChaCha20-Poly1305
- Эффективность: Читается только необходимая информация
- Целостность: Проверка чексуммы всего файла
- Аутентификация: HMAC проверка пароля
-
Пароль пользователя → Argon2id → Master Key (32 байта)
- Используется соль 16 байт
- Параметры по умолчанию: память 256KB, итерации 5, параллелизм 1
-
Master Key → Генерация и шифрование → Encrypted FEK (60 байт)
- Генерируется случайный FEK (File Encryption Key) 32 байта
- FEK шифруется Master Key с помощью ChaCha20-Poly1305
- Результат: nonce(12) + ciphertext(32) + auth_tag(16) = 60 байт
-
Master Key → HMAC → Verification Tag (16 байт)
- Вычисляется HMAC от аутентифицируемых полей заголовка
- Используется для проверки правильности пароля
Для небольших данных (текст):
- Режим: ChaCha20-Poly1305 (AEAD)
- Nonce: 12 байт (случайный для каждого секрета)
- Ключ: FEK
- Результат: зашифрованные данные с встроенным тегом аутентификации
Для больших файлов:
- Режим: XChaCha20-Poly1305 (потоковое шифрование)
- Nonce: 24 байта (базовый nonce + счетчик блока)
- Размер блока: 128KB
- Каждый блок шифруется отдельно с уникальным nonce
┌─────────────────────────┐ ← offset 0
│ File Header │ (256 байт, фиксированный размер)
│ (всегда открыт) │
├─────────────────────────┤ ← offset 256
│ Зашифрованный │
│ Секрет #1 │ (размер переменный)
├─────────────────────────┤
│ Зашифрованный │
│ Секрет #2 │ (размер переменный)
├─────────────────────────┤
│ ... │
├─────────────────────────┤ ← IndexTableOffset
│ Зашифрованная │
│ Индексная таблица │ (содержит метаданные всех секретов)
└─────────────────────────┘ ← конец файла
type Header struct {
Version uint8 // Версия формата файла (0x01)
Flags uint8 // Флаги состояния (завершен, зашифрован, сжат)
EncryptionAlgo uint8 // Алгоритм шифрования (1 = ChaCha20)
ArgonMemoryLog2 uint8 // log2(память в KB) для Argon2
SecretCount uint32 // Количество секретов
CreatedAt int64 // Время создания (unix timestamp)
ModifiedAt int64 // Время последней модификации
DataSize uint64 // Общий размер всех секретов
OwnerID [16]byte // UUID владельца файла
ArgonSalt [16]byte // Соль для Argon2
ArgonIterations uint16 // Количество итераций Argon2
ArgonParallelism uint8 // Параллелизм Argon2
_ uint8 // Байт выравнивания
Checksum [32]byte // SHA256 чексумма всего файла
VerificationTag [16]byte // HMAC для проверки пароля
EncryptedFEK [60]byte // Зашифрованный FEK
IndexTableOffset uint64 // Смещение индексной таблицы
IndexTableNonce [12]byte // Nonce для шифрования индекса
Reserved [60]byte // Резерв для будущих расширений
}type SecretMeta struct {
Name [32]byte // Имя секрета (фиксированный размер)
Offset uint64 // Смещение в файле
Size uint64 // Размер зашифрованных данных
CreatedAt uint64 // Время создания
ModifiedAt uint64 // Время изменения
Type uint8 // Тип: 1=текст, 2=файл, 3=бинарные данные
Flags uint8 // Флаги: завершен, зашифрован, сжат, удален
_ [1]byte // Выравнивание
Nonce [24]byte // IV для шифрования (12 или 24 байта)
EncryptMode uint8 // Режим шифрования: 1=AEAD, 2=потоковый
}-
Инициализация:
Пользователь → пароль → Argon2id(пароль, соль, параметры) → Master Key -
Генерация ключей:
Генерация FEK (32 случайных байта) ChaCha20(Master Key, nonce) → Encrypted FEK HMAC(Master Key, заголовок) → Verification Tag -
Создание заголовка:
- Заполнение всех полей структуры Header
- Установка флагов, времени, параметров Argon2
- Запись заголовка в начало файла
-
Подготовка:
- Проверка пароля через Verification Tag
- Расшифровка FEK из Encrypted FEK
-
Шифрование данных:
Для текста: ChaCha20-Poly1305(FEK, nonce_12, данные) → зашифрованные_данные Для файлов: XChaCha20-Poly1305_Stream(FEK, nonce_24, поток_данных) → зашифрованный_поток -
Запись в контейнер:
- Поиск конца данных (после последнего секрета)
- Запись зашифрованных данных
- Создание метаданных SecretMeta
- Добавление в индексную таблицу
-
Обновление метаинформации:
- Увеличение SecretCount в заголовке
- Обновление DataSize
- Пересчет и запись новой чексуммы
-
Аутентификация:
Пароль → Argon2id → Master Key HMAC(Master Key, заголовок) == Verification Tag ? Если да: Master Key правильный -
Получение FEK:
ChaCha20-Poly1305.Decrypt(Master Key, Encrypted FEK) → FEK -
Поиск секрета:
- Расшифровка индексной таблицы с помощью FEK
- Поиск секрета по имени в индексе
- Получение offset и size
-
Расшифровка данных:
Переход к offset в файле Чтение size байт зашифрованных данных Для AEAD режима: ChaCha20-Poly1305.Decrypt(FEK, nonce, данные) → исходные_данные Для потокового режима: XChaCha20-Poly1305_Stream.Decrypt(FEK, nonce, поток) → исходный_поток
- Установка флага
FlagDeletedв метаданных - Секрет остается в файле, но помечается как удаленный
- Не показывается в списках
Проблема: После удаления секрета в файле остается "дыра"
[Header][Secret1][Secret2][Secret3][Index] → удаляем Secret2
[Header][Secret1][ ??? ][Secret3][Index] ← дыра в файле
Решение: Дефрагментация со сдвигом данных влево
-
Зануление удаляемой области:
for remain > 0 { // Записываем нули блоками по 4MB seek(удаляемый_offset + позиция) write(нулевой_буфер[размер_блока]) позиция += размер_блока remain -= размер_блока }
-
Сдвиг секретов влево:
for каждый_секрет_после_удаленного { // Читаем блоками и переписываем левее for remain > 0 { seek(исходный_offset) read(буфер[размер_блока]) seek(новый_offset) write(буфер[размер_блока]) исходный_offset += размер_блока новый_offset += размер_блока } // Обновляем offset в метаданных секрет.Offset = новый_offset_секрета }
-
Обрезка файла:
новый_конец = максимальный_offset + размер_последнего_секрета truncate(файл, новый_конец)
-
Обновление метаданных:
- Удаление записи из индексной таблицы
- Уменьшение SecretCount и DataSize
- Пересчет чексуммы
-
Вычисление чексуммы:
hasher = SHA256.New() // Хешируем заголовок БЕЗ поля Checksum hasher.Write(заголовок_до_checksum) hasher.Write([32]byte{0}) // нули вместо checksum hasher.Write(заголовок_после_checksum) // Хешируем все данные после заголовка seek(256) // после заголовка for блок := range все_данные { hasher.Write(блок) } computed_checksum = hasher.Sum()
-
Сравнение:
if computed_checksum != header.Checksum { return ОшибкаЦелостности }
-
Подготовка индексной таблицы:
payload_end = максимальный(секрет.Offset + секрет.Size) header.IndexTableOffset = payload_end -
Пересчет HMAC:
header.VerificationTag = HMAC(Master Key, header.AuthenticatedBytes()) -
Шифрование и запись индекса:
зашифрованный_индекс = ChaCha20(FEK, IndexTableNonce, сериализованный_индекс) seek(IndexTableOffset) write(зашифрованный_индекс) -
Установка флагов завершения:
header.Flags = FlagCompleted | FlagEncrypted -
Двухэтапная запись заголовка:
// Этап 1: запись без чексуммы header.Checksum = [32]byte{0} seek(0) write(header) // Вычисление чексуммы всего файла checksum = вычислить_SHA256_всего_файла() // Этап 2: запись с правильной чексуммой header.Checksum = checksum seek(0) write(header) -
Синхронизация:
file.Sync() // принудительная запись на диск
- Перебор паролей: Затруднен Argon2id с настраиваемой сложностью
- Изменение данных: Обнаруживается через чексумму SHA256
- Подмена секретов: Невозможна из-за AEAD тегов аутентификации
- Анализ трафика: Все данные зашифрованы, включая метаданные
- Конфиденциальность: ChaCha20 с 256-битными ключами
- Аутентичность: Poly1305 теги для каждого секрета
- Целостность: SHA256 чексумма всего контейнера
- Неотказуемость: HMAC подпись мастер-ключом
- Пароли не хранятся в открытом виде
- FEK генерируется криптостойким ГПСЧ
- Каждый секрет имеет уникальный nonce
- Master Key выводится детерминированно из пароля
- Ленивая загрузка: Читается только заголовок и индекс
- Потоковое шифрование: Большие файлы обрабатываются блоками
- Блочные операции: Дефрагментация работает блоками 4MB
- Прогресс-индикаторы: Для длительных операций
- Поддержка файлов до петабайт
- Эффективная работа с тысячами секретов
- Минимальное потребление памяти
- Инкрементальные обновления
# Создание нового контейнера
./zec new --file=my_secrets
# Добавление текстового секрета
./zec add --file=my_secrets --name=api_key --payload="secret_key_123"
# Добавление файла
./zec add --file=my_secrets --name=backup --payload="/path/to/backup.tar.gz"
# Просмотр содержимого
./zec list --file=my_secrets
# Чтение секрета
./zec get --file=my_secrets --name=api_key
# Экспорт файла
./zec get --file=my_secrets --name=backup --out=./restored_backup.tar.gz
# Удаление секрета (мягкое)
./zec rm --file=my_secrets --name=old_key
# Принудительное удаление с дефрагментацией
./zec rm --file=my_secrets --name=large_file --force
# Просмотр заголовка файла
./zec header --file=my_secrets- ✅ Создание и управление контейнерами
- ✅ Шифрование ChaCha20-Poly1305
- ✅ Потоковое шифрование больших файлов
- ✅ Дефрагментация при удалении
- ✅ Проверка целостности
- ✅ CLI интерфейс
- 🔄 Рефакторинг в чистую библиотеку
- 📱 Web интерфейс
- 🔧 Интерактивная оболочка
- 🤖 Агент для управления сессиями
- 🗜️ Сжатие данных (zstd)
- 🔐 Дополнительные алгоритмы шифрования
- ☁️ Поддержка облачных хранилищ
- 👥 Многопользовательский режим
ZEC реализует современный подход к безопасному хранению секретов, сочетая:
- Криптографически стойкие алгоритмы (ChaCha20, Argon2, HMAC)
- Эффективную файловую структуру с индексированием
- Надежную дефрагментацию при удалении данных
- Полную проверку целостности
- Удобный пользовательский интерфейс
Система спроектирована для обеспечения максимальной безопасности при сохранении высокой производительности и удобства использования.