Обновление: Часть 2 вышла!

На этой неделе мы с моим товарищем по команде Рамом Вальским получили уникальное задание: преобразовать модуль, связанный с криптографией, который мы написали на Node, в JavaScript с помощью WebCrypto. Поскольку мы с Рамом любим все, что связано с криптовалютой, наша реакция была следующей:

Краткое введение в PBKDF2

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

Когда он используется?
Функция получения ключа обычно используется для защиты хранилища паролей и для создания криптографического ключа из пароля или ключевой фразы (т. е. растяжения ключа).

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

Получение ключа PBKDF2

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

Начнем с создания подписи для нашей функции getDerivation:

Чтобы получить ключ с помощью WebCrypto, нам нужно было использовать функцию deriveBits объекта crypto.subtle. Однако был один нюанс: deriveBits секретный пароль нужен как CryptoKey объект, а у нас была только строка 😕

Просматривая документацию по WebCrypto в MDN, мы заметили функцию importKey. Эта функция в основном возвращает обещание, которое преобразуется в объект aCryptoKey на основе его параметра. Посмотрев на список входных параметров, мы заметили кое-что важное: пароль должен быть либо ArrayBuffer, либо JSONWebKey. Здесь в игру вступает TextEncoder class. TextEncoder предоставляет функцию encode, которая принимает строковый параметр и возвращает объект Uint8Array, который мы затем можем использовать для функции importKey!

Теперь, когда мы преобразовали наш пароль в aCryptoKey, мы можем использовать его для получения хешированного ключа. Как упоминалось выше, мы собираемся использовать для этого функцию deriveBits. Перед вызовом функции deriveBits мы должны создать объект, содержащий ее конфигурацию:

👉 Обратите внимание, что, как и функция importKey, deriveBits требует, чтобы соль передавалась как ArrayBuffer, а не как простая строка.

Для вызова deriveBits требуются три параметра:

  1. Объект параметров, который мы создали ранее
  2. Секретный ключ, который мы импортировали ранее
  3. Количество бит, которое мы хотим получить

😱 Вы можете спросить - почему мы умножили keyLength на 8?

Если вы помните, в начале поста я упоминал, что мы собираемся расшифровать объект, который был зашифрован в NodeJS. NodeJS использует байты, поэтому длина ключа, который мы использовали (48), на самом деле составляла 48 байтов.
Однако WebCrypto использует биты. Таким образом, чтобы сообщить WebCrypto, что мы хотим получить 48 байтов из производной, мы умножаем ее на 8 и получаем битовое значение длины ключа (48 * 8 = 384).

Подведем итоги!

Синтаксис WebCrypto иногда может показаться странным, но на самом деле им довольно легко пользоваться. Мы обнаружили, что использование битов вместо строк упрощает работу, поскольку в процессе с ними не производится преобразование.

Во второй части мы опишем, как использовать полученный нами ключ PBKDF2 и использовать его с модулями шифрования и дешифрования WebCrypto.

🔥 Особая благодарность Раму Вальскому и Бараку Амару за помощь в написании этого сообщения!