У каждого есть свои тайны и человечество вечно пыталось находить пути как их скрытия так и разоблачения.
Под катом будет описан один из способов скрыть свои файлы так, чтобы ваш параноик был счастлив
Не могу сказать, что я параноик до мозга костей, но бывают случаи когда мне нужно быть уверенным, что информация не попадет куда не надо, но при этом и не потеряется.
Хочу заметить, что я не претендую на звание "уникальный проект", есть 100500 похожих проектов со своими плюсами и минусами.
Краткое описание
Как видно из названия, dropcryptbox работает поверх dropbox используя oauth. Все операции происходят исключительно между машиной пользователя и api дропбокса минуя третьих посредников, а это значит, что приложение не имеет своего бекенда.
Файлы шифруются и расшифровываются на машине пользователя тем самым гарантируя, что оригинальный файл не попадет на какие-либо сервера.
Генерация ключа
Ключ шифрования создается на основе мастер пароля который нужно ввести при открытии страницы. Для начала получаем sha256 хеш от masterPassword, затем генерируем pbkdf2 ключ на основе полученного хеша(sha256), pbkdf2 (HMAC-SHA1, 256 бит, 1000 итераций)
import sha256 from "crypto-js/sha256";
import pbkdf2 from "crypto-js/pbkdf2";
import HEX_ENCODING from "crypto-js/enc-hex";
export const PBKDF2 = pbkdf2;
export const SHA256 = sha256;
export const salt = SHA256("CRYPTODROPBOX_SOME_SALT");
export function getKey(str){
let hash = PBKDF2(SHA256(str), salt, { keySize: 256/32, iterations: 1000 });
return new Buffer(hash.toString(HEX_ENCODING), 'hex');
}
Шифрование файлов
Для шифрования файлов используется chacha20.
Всё шифрование в приложении происходит по алгоритму:
- Генерируется рандомный nonce
- Шифрование на основе полученного ключа(от мастер пароля)
- nonce добавляется в начало шифротекста
Имя файла также шифруется по алгоритму выше, результат преобразовывается в hex и добавляется окончание .ar
import chachaAsync from './chacha20'; // расширенная версия chacha для получение прогресса шифрования
import chacha from 'chacha20';
import { sync as randomBytesSync } from 'random-bytes';
export const NONCE_SIZE = 8;
export const KEY_SIZE = 32;
export function generateNonce(){
return randomBytesSync(NONCE_SIZE);
}
export function encrypt(buffer, key, nonce, options = {}){
let encrypted = chacha.encrypt(key, nonce, buffer);
if(options.joinNonce)
encrypted = Buffer.concat([nonce, encrypted]);
return encrypted;
}
export function decrypt(buffer, key, nonce){
return chacha.decrypt(key, nonce, buffer);
}
export function createEncryptJob(buffer, key, nonce){
return chachaAsync.encrypt(buffer, key, nonce);
}
export function createDecryptJob(buffer, key, nonce){
return chachaAsync.decrypt(buffer, key, nonce);
}
Детали
Для ui используется react + redux, webpack, es6 + es7 decorators. Чтобы не нагружать основной поток — отправка, скачивание, шифрование и расшифровка файлов выполняются в отдельном потоке (webworkers).
На данный момент поддерживаются только новейшие версии firefox и chrome
Демо. Совсем скоро код будет размещен на GitHub.
Комментарии (27)
EvilGenius18
05.01.2017 15:15+1А почему решили делать эксклюзивно для дропбокс?
Не легче ли просто зашифрованные файлы ложить в папку синхронизации на пк, которые потом уже в шифрованном виде отгружаются на дропбокс?
Таким образом можно было бы использовать для всех облачных хранилищ одновременноkwolfy
05.01.2017 15:22Была цель реализовать проект насколько возможно быстро — поэтому dropbox
Ваш вариант тоже интересен, но у меня есть много идей которые требуют взаимодействия с именно файловым сервером, а не локальной папкой
ooprizrakoo
05.01.2017 17:06+2Дядь, а дядь. Ну подумай немножко шире, как твой пост выглядит в хабраленте?
та
Scratch
05.01.2017 17:13Шифровать все файлы одним ключом нехорошо. Лучше генерируйте рандомный chacha ключ (надеюсь, там poly1305 используется? ) для каждого файла и уже его шифруйте своим мастер ключом из KDF. А потом пишите его вначало файла.
Без HMAC типа poly1305 любой файл можно поменять и вы этого не поймете при расшифровке. Зашифрованный ключ файла можно добавить в additional data режима AEAD, если используете chacha20-poly1305.kwolfy
05.01.2017 17:17Используется chacha20-poly1305.
В чём принципиальная разница между тем шифровать всё одним ключем или разными ключами когда у нас и так есть уникальный nonce?
agee
05.01.2017 20:06+2О хорошем: во-первых, спасибо за работу :)
Теперь о замечаниях, не посчитайте за придирки. Статья для крипто-темы оочень маленькая.
0) Исходный код.
1)
не претендую на звание "уникальный проект", есть 100500 похожих проектов со своими плюсами и минусами
Вот эту часть как раз таки нужно подробно описать. Примеры готовых решений? Чем они не устроили? Чем Ваше решение лучше или хотя бы не хуже существующих?
2) Описание схемы шифрования не должно быть ограничено одним предложением. Из него совсем не понятно, насколько глубоко Вы владеете темой. Я вот, например, с поточными шифрами не работал. Чего там происходит вообще? Я имею в виду сам процесс, это же самое важное и интересное.
3) Каков сам процесс расшифровки, вообще не понятно. Почему выбран поточный шифр? Данные расшифровываются на лету? Если да, то этого никак не понять из статьи (ну может, я не углядел). Если нет, то почему не блочный шифр?
export function encrypt(buffer, key, nonce, options = {})
Что там в качестве буфера передается?
4) Там в коде какие-то импорты из "crypto-js". Это какая-то библиотека? Почему ей можно доверять?
5) Ни слова о MAC. Приходится из комментариев вылавливать, что используется poly1305. В коде не видно этого :(
6)
export const salt = SHA256("CRYPTODROPBOX_SOME_SALT");
У Вас константная соль? Какой в ней смысл вообще? Смотрю https://www.ietf.org/rfc/rfc2898.txt и максимум, что приходит на ум это часть
If a random number generator or pseudorandom generator is not
available, a deterministic alternative for generating the salt (or
the random part of it) is to apply a password-based key derivation
function to the password and the message M to be processed. For
instance, the salt could be computed with a key derivation function
as S = KDF (P, M). This approach is not recommended if the message M
is known to belong to a small message space (e.g., "Yes" or "No"),
however, since then there will only be a small number of possible
salts.В остальных случаях она вообще случайная должна быть. Доступ к PRNG вроде есть. (
import { sync as randomBytesSync } from 'random-bytes';-bytes';
)
7)
В коде
let hash = PBKDF2(SHA256(str), salt, { keySize: 256/32, iterations: 1000 });
В чем смысл шагаSHA256(str)
?kwolfy
06.01.2017 18:34Спасибо за здоровую критику.
Вообще всё это тема для следующей статьи, а сейчас отвечу кратно на некоторые вопросы.
1. На данный момент никаких преимуществ нет, но скоро будут новые функции которые раскроют подробнее эту тему
2,3. Поточный шифр только из соображений производительности т.к. на данный момент chacha20 работает быстрее aes, но в будущем расшифровка на лету тоже не исключение
4. crypto-js — это самая популярная браузерная криптобиблиотека на данный момент, причина доверять ей — открытый код
5. Статья писалась за час и была сразу опубликована, изначально расчет идет на более глубокое продолжение, помилуйте, не могу же я раскрыть все свои темы в одной статье))
6,7. Смысл sha256 — усложнение перебора, хоть и сомнительная операция. Константная соль, потому что в следующей версии планируется вообще заменить pbkdf2 на scryptagee
07.01.2017 04:12Переход на scrypt не отменяет генерации рэндомной соли. Кстати, scrypt внутри себя использует pbkdf2))
https://tools.ietf.org/html/rfc7914#page-3kwolfy
07.01.2017 12:55В данном случае, нам нужно чтобы для одного пароля был один и тот же ключ — всегда. Поэтому рандомная соль не подходит, а преимущество псевдорандомной соли(основанной на пароле) перед константной сомнительна
agee
07.01.2017 13:19Вам ничего не мешает сгенернную соль прикрепить к шифротексту так, как Вы поступаете с nonce и MAC. Вы говорите, преимущества рэндомной соли сомнительно, а в документе, который я привел, говорят обратное. Более того, соль как минимум должна быть уникальной, но уж точно не константной. В этом и вообще ее смысл ведь!
Почему ключ должен быть один и тот же для одного пароля? По-моему главное, чтобы коюч подходил к конкретному шифротексту, а не ко всем! Ведь если (теоретически, конечно ;), скомпрометируется один ключ, остальные шифротексты останутся целымиkwolfy
07.01.2017 13:39В будущем планируется добавить функцию шаринга файлов по ссылке и для каждого файла и так будет свой собственный ключ.
В контексте когда вы не знаете об этой функции — вы правы, а вот я знаю.
Пока писал коммент понял, что не прав.
Основная функция соли — усложнить создание радужных таблиц путем уникализации хеш-функции для каждого пользователя. Вся проблема была в том, что у приложения нет базы данных, а я не рассматривал компроментацию аккаунтов dropbox пачкой, хотя следовало
kwolfy
07.01.2017 13:08Если уже сильно придираться, один из рассматриваемых вариантов — при первой авторизации генерировать рандомную соль, шифровать его ключем на основе константной соли и записывать как файл в dropbox, а при следующей авторизации — получать этот файл, расшифровать и на основе этой соли уже получить ключ. Но и это на мой взгляд не особо эффективно.
Есть есть какие то предложения по этому поводу, рад буду услышать
lexore
06.01.2017 15:43Под катом будет описан один из способов скрыть свои файлы так, чтобы ваш параноик был счастлив.
Открываю сайт...
Для того чтобы воспользоваться сервисом, необходимо авторизоваться с помощью Dropbox OAuth.
CryptDropibox хочет получить доступ к своей папке Приложения › EnryptedFiles в вашем Dropbox.Хм, хм…
А что делать, если мой параноик не хочет давать стороннему сайту dropcryptbox.com доступ к своей папке в Dropbox?kwolfy
06.01.2017 18:35Скоро будет открыт код и каждый сможет у себя развернуть свою локальную версию
rhamdeew
Интересный вариант. Лично я поступил по-другому. Создал encfs-раздел на локальной системе. В Dropbox положил зашифрованный каталог. В итоге получается что в сторадже есть и обычные нешифрованные файлы и папка с "кракозябрами"
darken99
Плюс есть куча клиентов для всех платформ.
Сам использую этот вариант.
Goodkat
А не подскажете удобный клиент для iOS?
Я пользуюсь старым приложением Boxcryptor, которое не обновлялось уже три года, и, боюсь, может в любой момент перестать работать или вылететь из App Store, так как разработчик продвигает новое приложение, которое работает иначе.
darken99
Я пользуюсь KeePass Touch
Goodkat
Мы точно говорим о файловом менеджере, работающим с Dropbox и поддерживающим шифрованную файловую систему encfs, о которой написал rhamdeew в комментарии выше?
Судя по описанию, KeePass Touch — это менеджер паролей.
darken99
Извиняюсь, использую BoxCryptor