В чем разница между salt и initialization vector (iv) при шифровании данных?

161
13 сентября 2019, 21:40

На русском информации практически нет, а с английским у меня не очень хорошо. Подскажите, пожалуйста, в чем разница между этими двумя понятиями. Особенно меня интересует шифрование данных.

Я раньше никогда ничего не шифровал, только хешировал. Я знаю, что соль - это случайная строка, которая хранится в файле конфига. Соль нужно добавлять перед / после строки, которая будет хешироваться - чтобы сложнее было разгадать хеш. Соль неизвестна и исходный текст (по которому получился хеш) подобрать, хешируя разные строки, нельзя. Я написал про хеширование. Но ведь у соли будет тот же смысл и с шифрованием?

Я хочу научиться пользоваться функцией openssl_encrypt (это функция PHP). Там нет соли, там есть initialization vector. Я хочу понять, что это такое. И чем это отличается от соли.

Answer 1

"Salt" и "Init vector" - вещи разные и вообще несвязанные. Соль используется при подготовке ключа, тогда как инициализационный вектор нужен для связывания блоков. Будем разбираться с ними отдельно и по порядку.

Соль (salt)

Для того, чтобы из текстового пароля произвольной длины сделать двоичный ключ фиксированной длины, который нужен для шифрующего алгоритма, используется хеш-функция.

key = hash("password")

В принципе этого достаточно, но что произойдет, если оппонент каким-то образом получит ключ? Хотя пароль он не узнает (хеш-функция по определению является односторонней), ему этого и не нужно - все файлы, зашифрованные данным паролем, окажутся доступны. Они ведь зашифрованы тем же самым ключом, и неважно из какого пароля этот ключ получен.

Для того, чтобы избежать такой ситуации, используется чуть более сложная система генерации ключа:

key = hash("password" + случайное_число)

Вот это самое "случайное число" и называется солью. Это число не является секретом, и обычно передается открыто в заголовке зашифрованного файла. Как это работает? Допустим, мы зашифровали два файла одним паролем. Ключи для них будут сгенерированы следующим образом:

key1 = hash("password" + salt1)
key2 = hash("password" + salt2)

Если оппонент получил key1, он не может получить из него пароль (как и в простой схеме). Он также не может получить key2, поскольку общего между этими ключами только пароль, который получить из первого ключа невозможно. Таким образом, даже при компрометации одного ключа, сам пароль, и другие зашифрованные файлы никак не пострадают.

Соль даже может не быть большой. Для небольших объемов шифрования вполне может хватить даже обычного целого числа (которое даст 4 миллиарда вариаций). 64-битовое число будет достаточным в любом случае.

В принципе можно придумать и более сложные схемы, например, соль можно шифровать простым хешем пароля. Сложно сказать, добавит ли это прочности шифру с математической точки зрения, но мороки оппоненту будет точно больше.

Инициализационный вектор (initialization vector, IV)

Симметричные шифры бывают блочными и потоковыми, при этом потоковые практически не используются. Блочный же шифр шифрует строго один блок, обычно 8, 16, или 32 байта. Исходный поток дополняется (padded) до размера, кратного размеру блока, или даже полностью декорируется (пример). Затем декорированный поток шифруется, блок за блоком.

Эта самая древняя и простая схема называется ECB (Electronic Code Book). Проблема с ней в том, что одинаковые входные данные дают одинаковые зашифрованные блоки. Вопрос тут не столько в надежности шифра (ECB достаточно надежен), сколько в том, что из анализа одинаковых блоков можно делать какие-то выводы о содержании файла. Особенно эта проблема актуальна для шифров с небольшим размером блока - DES, Triple-DES, Blowfish, и т.д.

Для борьбы с этим используется метод связывания блоков (block chaining). В простейшем случае это просто XOR с предыдущим блоком. Методов связывания придумали целую пачку - CBC, CFB, OFB, CTR - если интересно, можете почитать здесь.

Так вот, к чему все это - все методы связывания основаны на том, что текущий блок как-то замешивается с предыдущим. Есть вариации, но для простоты можно сказать так. Поэтому естественным образом возникает вопрос - с чем замешивать самый первый блок? Ответ: с неким искусственно сгенерированным "нулевым" блоком, который называется "инициализационным вектором". Вот, собственно, и все, что он из себя представляет - просто случайный стартовый блок для связывания.

Как и соль, IV не является секретом и передается в заголовке файла. Причем даже необязательно, чтобы он был случайным - нулевой IV точно так же закроет идентичные блоки. Но конечно, правильный IV должен генерироваться случайно. Его можно шифровать как и соль, от этого будет только лучше.

READ ALSO
Сравнивание 2х массивов из 10 000 000

Сравнивание 2х массивов из 10 000 000

пишу на php, и есть реальная задача сравнения 2х огромных массивов данныхНо загвостка в том, что эти массивы разбиты по файлам

121
Создание плагина wordpress

Создание плагина wordpress

Пишу плагин форма обратной связи, соответственно нужно обрабатывать отправку формы, в связи с этим вопрос, в wordpress есть хук отвечающий за отправку...

125
Зачем добавлять noexcept к функциям с constexpr?

Зачем добавлять noexcept к функциям с constexpr?

В стандартной библиотеке MSVC 2017 поставляется примерно такая реализация std::size:

118
Обработать wheelEvent в QtGraphicsView

Обработать wheelEvent в QtGraphicsView

Хочу реализовать масштабирование в QtGraphicsView через колесико мышиНа данный момент делаю это через фильтр событий следующим способом:

138