
Привет, Хабр!
Я написал статью и преждевременно её публикую. Изначально планировал писать после завершения проекта, но до окончания осталось ещё пару месяцев, поэтому решил не терять время и написать статью пока информация свежа в голове. К тому же, в большей степени пишу для себя. :) В одном из моих последних проектов, который я разрабатываю как open source, я реализовал сквозное (end-to-end) шифрование — аналогично тому, как это делают, например, WhatsApp или Telegram.
В этой статье мы углубимся в реализацию шифрования сообщений на стороне клиента с использованием JavaScript и Web Crypto API, разобрав практический пример, который будет в самом конце статьи.
Начнём с того, что если вы полный ноль в криптографии, то понять написанное здесь может быть непросто. Мне самому, несмотря на 10 лет опыта в разработке, пришлось поломать голову — всё, что здесь происходит, это чистая математика, о которой мы в этой статье говорить не будем :) Особо впечатлительные могут подумать, что это магия :)
Если вкратце, без сложных слов и терминов, попробую объяснить суть сквозного (end-to-end) шифрования
Магия шифрования в трёх ключах

Фундамент, на котором всё держится, — это три ключа. Возвращайтесь к этому месту, если что-то окажется непонятным.
Приватный ключ (Private Key): Хранится (в зашифрованном виде).
Публичный ключ (Public Key): Доступен абсолютно всем.
Общий секретный ключ (Shared Secret / Symmetric Key): Генерируется на основе вашего приватного ключа + публичного ключа вашего собеседника. Именно этот ключ используется для непосредственного шифрования и дешифрования сообщений.
Комбинация вашего приватного ключа + публичного ключа вашего собеседника позволяет получить общий секретный ключ (в нашем примере ниже это будет ключ AES). Благодаря этому общему секретному ключу вы можете шифровать и расшифровывать сообщения.
Приватный и публичный ключи можно хранить в базе данных, но есть нюанс с приватным ключом. Сам приватный ключ не рекомендуется хранить в открытом виде; его нужно дополнительно зашифровать паролем пользователя или любым другим ключевым словом (в рамках мессенджера это, как правило, пароль пользователя). Публичный ключ мы храним в открытом виде.
Общий секретный ключ (тот, что в коде нижеthis.aesKey
) мы не храним в базе данных. Он генерируется (вычисляется) каждый раз при инициализации чата с конкретным контактом. Здесь может возникнуть недоумение: как же мы будем расшифровывать сообщения, если этот ключ не хранится, а генерируется заново? В этом и заключается "магия" асимметричного шифрования и протокола обмена ключами.

Когда вы открываете чат с контактом, ваш клиент заново вычисляет этот общий секретный ключ, как вы уже знаете вот таким образом (Ваш приватный ключ + Публичный ключ вашего контакта = Общий секретный ключ). С помощью этого ключа вы шифруете новые сообщения и расшифровываете все предыдущие сообщения в этом чате, так как они были зашифрованы тем же самым общим секретным ключом. Голову можно ломать долго и безрезультатно, пока не поймем, что такое асимметричное шифрование и протоколы обмена ключами.
Асимметричное шифрование и ECDH
Асимметричное шифрование использует пару ключей: публичный (public) и приватный (private). Публичный ключ можно свободно распространять, в то время как приватный ключ должен храниться его владельцем в секрете то есть в зашифрованном виде.
ECDH (Elliptic Curve Diffie-Hellman) – это протокол обмена ключами, основанный на математике эллиптических кривых. Он позволяет двум сторонам, каждая из которых имеет свою пару ECDH-ключей (приватный и публичный), установить общий секретный ключ через незащищенный канал. Важно, что третья сторона, даже перехватив их публичные ключи, не сможет вычислить этот общий секрет. В нашем примере используется кривая P-256 – популярный и надежный стандарт.
Думаю, немногие поняли то, что только что прочитали. Всё, что вам нужно понять на данном этапе, – это то, что технология работает :) Позже пазл сложится, возможно, после повторного прочтения. А теперь немного о встроенных технологиях браузера.
Web Crypto API
Web Crypto API – это встроенный в браузеры интерфейс JavaScript, предоставляющий доступ к низкоуровневым криптографическим примитивам. Он позволяет выполнять такие операции, как хеширование, генерация подписей, шифрование и дешифрование. Использование Web Crypto API предпочтительнее сторонних библиотек для основных криптографических операций, так как оно часто аппаратно ускорено и тщательно проверено на безопасность. Все операции Web Crypto API асинхронны и возвращают Promise
.
А теперь перейдем к практическому анализу. Я создал класс ChatCrypto
, который мы рассмотрим подробнее:
class ChatCrypto {
constructor(myPrivateKeyBase64, theirPublicKeyBase64) {
this.myPrivateKeyBase64 = myPrivateKeyBase64;
this.theirPublicKeyBase64 = theirPublicKeyBase64;
this.aesKey = null; // Здесь будет храниться общий симметричный ключ AES
}
static base64ToArrayBuffer(base64) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes.buffer;
}
static arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let b of bytes) {
binary += String.fromCharCode(b);
}
return btoa(binary);
}
init() {
// Преобразуем ключи из Base64 в ArrayBuffer
const privateRaw = ChatCrypto.base64ToArrayBuffer(this.myPrivateKeyBase64);
const publicRaw = ChatCrypto.base64ToArrayBuffer(this.theirPublicKeyBase64);
// Замечание: Следующие строки для разбора publicRaw на x и y координаты,
// и сборка uncompressedPoint могут быть специфичны для определенного формата
// представления "сырого" открытого ключа. Если publicRaw уже в формате SPKI,
// они могут не понадобиться, так как crypto.subtle.importKey("spki", ...)
// ожидает стандартную структуру.
// const x = publicRaw.slice(0, publicRaw.byteLength / 2);
// const y = publicRaw.slice(publicRaw.byteLength / 2);
// const uncompressedPoint = new Uint8Array([0x04, ...new Uint8Array(x), ...new Uint8Array(y)]);
// Импортируем наш приватный ключ
return crypto.subtle.importKey(
"pkcs8", // Формат приватного ключа (стандартный)
privateRaw,
{ name: "ECDH", namedCurve: "P-256" }, // Алгоритм и параметры
false, // Неэкспортируемый
["deriveBits"] // Разрешенное использование: для вывода бит (общего секрета)
).then(privateKey => {
// Импортируем публичный ключ собеседника
return crypto.subtle.importKey(
"spki", // Формат публичного ключа (стандартный)
publicRaw,
{ name: "ECDH", namedCurve: "P-256" },
false, // Неэкспортируемый
[] // Для публичного ключа в ECDH здесь специфические использования не нужны
).then(publicKey => {
// 4. Вычисляем общий секрет (deriveBits)
return crypto.subtle.deriveBits(
{ name: "ECDH", public: publicKey }, // Указываем публичный ключ собеседника
privateKey, // Наш приватный ключ
256 // Длина выводимого секрета в битах
);
});
}).then(sharedBits => {
// Хешируем общий секрет для получения ключа AES (используем SHA-256 как KDF)
return crypto.subtle.digest("SHA-256", sharedBits);
}).then(hashed => {
// Импортируем хешированный секрет как ключ AES-GCM
return crypto.subtle.importKey(
"raw", // Формат "сырых" байт
hashed, // Хешированный секрет
{ name: "AES-GCM" }, // Алгоритм симметричного шифрования
false, // Неэкспортируемый
["encrypt", "decrypt"] // Разрешенные использования: шифрование и дешифрование
);
}).then(aesKey => {
this.aesKey = aesKey; // ✅ ВАЖНО! Сохраняем полученный ключ AES
return true; // Сигнализируем об успешной инициализации
});
}
encrypt(plaintext) {
if (!this.aesKey) return Promise.reject("ChatCrypto not initialized");
// Генерируем уникальный вектор инициализации (IV)
const iv = crypto.getRandomValues(new Uint8Array(12)); // 12 байт (96 бит) рекомендуется для AES-GCM
// Преобразуем текстовое сообщение в байты (UTF-8)
const encoded = new TextEncoder().encode(plaintext);
// Шифруем данные
return crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv }, // Алгоритм и IV
this.aesKey, // Наш общий ключ AES
encoded // Данные для шифрования
).then(encrypted => {
// 4. Возвращаем IV и зашифрованные данные (в Base64 для удобства передачи)
return {
iv: ChatCrypto.arrayBufferToBase64(iv),
data: ChatCrypto.arrayBufferToBase64(encrypted)
};
});
}
decrypt(cipherBase64, ivBase64) {
if (!this.aesKey) return Promise.reject("ChatCrypto not initialized");
// Преобразуем шифротекст и IV из Base64 в ArrayBuffer
const encrypted = ChatCrypto.base64ToArrayBuffer(cipherBase64);
const ivBuffer = ChatCrypto.base64ToArrayBuffer(ivBase64);
// Дешифруем данные
return crypto.subtle.decrypt(
{ name: "AES-GCM", iv: new Uint8Array(ivBuffer) }, // Алгоритм и IV (должен быть TypedArray)
this.aesKey, // Тот же общий ключ AES
encrypted // Зашифрованные данные
).then(decrypted => {
// Преобразуем расшифрованные байты обратно в строку
return new TextDecoder().decode(decrypted);
});
}
}
Конструктор constructor()
и метод base64ToArrayBuffer()
:
Конструктор принимает ваш приватный ключ и публичный ключ собеседника в формате Base64. Base64 – это способ кодирования бинарных данных в текстовую строку, удобный для передачи или хранения.
this.aesKey
инициализируется какnull
и будет заполнен после успешного выполнения методаinit()
.Статические методы
base64ToArrayBuffer
иarrayBufferToBase64
служат для преобразования данных между строками Base64 иArrayBuffer
(формат, с которым работает Web Crypto API).
Метод init()
: Установление общего ключа AES
Это сердце нашего класса, где происходит "магия" ECDH и создается общий ключ для симметричного шифрования.
Разбор шагов в init()
:
Преобразование ключей: Ключи из Base64 переводятся в
ArrayBuffer
.Импорт приватного ключа: Ваш приватный ключ импортируется в формате
pkcs8
. Указывается, что это ключ ECDH на кривой P-256 и он будет использоваться дляderiveBits
(вычисления общего секрета).Импорт публичного ключа собеседника: Публичный ключ собеседника импортируется в формате
spki
.Вычисление общего секрета (
deriveBits
): Это ключевой шаг ECDH. Используя ваш приватный ключ и публичный ключ собеседника,deriveBits
вычисляет общий секретный набор бит (sharedBits
). Этот секрет будет одинаковым у вас и вашего собеседника, если они используют свои соответствующие приватные ключи и публичные ключи друг друга.Хеширование общего секрета (
digest
):sharedBits
хешируются с помощью SHA-256. Это распространенная практика для преобразования выводаderiveBits
в криптографически стойкий ключ нужной длины для симметричного шифра (в данном случае AES). Этот шаг также служит как KDF (Key Derivation Function).Импорт ключа AES (
importKey
): Полученный хеш (hashed
) импортируется как "сырой" (raw
) ключ для алгоритма AES-GCM. Этот ключ (this.aesKey
) теперь готов к использованию для шифрования и дешифрования сообщений.
После успешного выполнения this.aesKey
будет содержать объект CryptoKey
, готовый к работе.
Метод encrypt(plaintext)
: Шифрование сообщения
Разбор шагов в encrypt()
:
Генерация IV (Initialization Vector): Создается случайный 12-байтный IV. Напоминаем, он должен быть уникальным для каждого шифрования этим же ключом.
Кодирование текста: Сообщение преобразуется из строки JavaScript в
Uint8Array
(последовательность байт в кодировке UTF-8) с помощьюTextEncoder
.Шифрование:
crypto.subtle.encrypt
выполняет шифрование данных с использованием AES-GCM, нашегоthis.aesKey
и сгенерированногоiv
.Возврат результата: Зашифрованные данные и IV (оба в Base64) возвращаются как объект. IV необходимо передать получателю вместе с шифротекстом, так как он потребуется для дешифрования.
Метод decrypt(cipherBase64, ivBase64)
: Дешифрование сообщения
Разбор шагов в decrypt()
:
Преобразование данных: Полученные шифротекст и IV (в Base64) преобразуются обратно в
ArrayBuffer
. Обратите внимание, что дляcrypto.subtle.decrypt
параметрiv
должен бытьTypedArray
(например,Uint8Array
), поэтому мы передаемnew Uint8Array(ivBuffer)
.Дешифрование:
crypto.subtle.decrypt
выполняет дешифрование. Важно, что AES-GCM не только расшифрует данные, но и проверит их целостность и аутентичность, используя тот жеaesKey
иiv
, которые использовались при шифровании. Если данные были подделаны, ключ не тот или IV не тот, методdecrypt
вернет ошибку (отклонитPromise
).Декодирование текста: Успешно расшифрованные байты преобразуются обратно в читаемую строку с помощью
TextDecoder
.
4. Как это работает вместе: Концептуальный поток
-
Генерация ключевых пар:
Пользователь А генерирует свою пару ECDH-ключей (публичный
PkA
и приватныйSkA
).Пользователь Б делает то же самое (публичный
PkB
и приватныйSkB
).Этот шаг в приведенном коде
ChatCrypto
не показан, но показан ниже в секции примеры (он выполняется один раз, например, при регистрации пользователя), он предшествует использованию класса. Web Crypto API имеет методcrypto.subtle.generateKey
для этого. Приватный ключSk
должен храниться надежно и быть зашифрован паролем пользователя.
-
Обмен публичными ключами:
Пользователь А передает свой публичный ключ
PkA
пользователю Б.Пользователь Б передает свой публичный ключ
PkB
пользователю А.Этот обмен должен быть надежным, чтобы избежать атаки "человек посередине" (Man-in-the-Middle, MitM). Например, через защищенный сервер или путем верификации отпечатков ключей.
-
Инициализация
ChatCrypto
и вычисление общего секретного ключа:У пользователя А:
const cryptoA = new ChatCrypto(SkA_base64, PkB_base64); await cryptoA.init();
У пользователя Б:
const cryptoB = new ChatCrypto(SkB_base64, PkA_base64); await cryptoB.init();
В результате у обоих (
cryptoA.aesKey
иcryptoB.aesKey
) будет вычислен одинаковый симметричный ключ AES.
-
Обмен сообщениями:
Пользователь А шифрует сообщение для Б:
const { iv, data } = await chatCryptoA.encrypt("Привет, Б!");
Затем А отправляет объект{ iv, data }
пользователю Б.Пользователь Б получает
{ iv, data }
и дешифрует:const message = await chatCryptoB.decrypt(data, iv); // message будет "Привет, Б!"
Пример: Шифрование текста encrypt()
// Иницилизация класса
let chatCrypto = new ChatCrypto( "Мой_приватный_ключ" , "Публичный_ключ_контакта" );
// Запускаем
chatCrypto.init().then(() => {
chatCrypto.encrypt("Текст").then(result => {
// Выведется зашифрованный текст
console.log(result);
});
});
Пример: Расшифровка текста decrypt()
// Иницилизация класса
let chatCrypto = new ChatCrypto( "Мой_приватный_ключ" , "Публичный_ключ_контакта" );
// Запускаем
chatCrypto.init()
.then( () => chatCrypto.decrypt("Зашифрованный_текст", "Векторный_ключ") )
.then(result => {
// Выведется расшифрованный текст
console.log(result);
})
Заключение
Мы рассмотрели, как можно реализовать надежное сквозное шифрование сообщений в JavaScript с использованием Web Crypto API. Комбинация ECDH для безопасного обмена ключами и AES-GCM для эффективного и аутентифицированного шифрования данных является мощным и современным подходом. Класс ChatCrypto
служит хорошим отправным примером такой реализации. Помните о важности безопасной генерации, хранения приватных ключей и надежного обмена публичными ключами для построения безопасной системы.
Комментарии (50)
nin-jin
29.05.2025 21:38Привет HabrGPT, перепиши код с промисов на асинхронные функции, вынеси функции работы с base64 в отдельный неймспейс, используй base64url кодирование, для генерации общего ключа используй функцию sharedKey, в качестве вектора инициализации используй хеш от сообщения и публичного ключа, для импорта/экспорта используй формат JWK.
valyasha19
29.05.2025 21:38Что за HabrGPT?
nin-jin
29.05.2025 21:38Автор большинства статей на Хабре в последнее вюемя.
valyasha19
29.05.2025 21:38Ну вы хотябы на вопрос можете ответить что такое HabrGPT? Нейросеть хабра или что? В интернете не нашел инфу.
nin-jin
29.05.2025 21:38Не sharedKey, а deriveKey, конечно же.
nazar_nazar
29.05.2025 21:38Не deriveKey а deriveBits в рамках класса который в статье
nin-jin
29.05.2025 21:38Вот в рамках конкретно этого класса должен быть именно deriveKey. В отличие от $mol_crypto_sacred, который является обёрткой над буфером, а не нативным ключом.
Vic1111
29.05.2025 21:38скажите, возможно ли шифрование голоса во время звонка на кнопрочных телефонах J2ME причем не через сервер а точка - точка ?
eimrine
29.05.2025 21:38Вы от него ещё и реал-тайм хотите? Скорее можно сказать фразу и долго-долго ждать, после того как притащите туда хоть какой-то современный кодек который по крайней мере уменьшит количество байт на зашифровку. Во время звонка я не уверен что можно с ж2ме пользоваться звуковым потоком.
Vic1111
29.05.2025 21:38ответ Алисы:
Нет, шифрование голоса на Java не сопровождается сильной задержкой. stackoverflow.comvk.com
Для реализации шифрования голосовых данных в Java используются библиотеки, которые обеспечивают быструю работу, например, класс
Cipher
с алгоритмом AES. Задержка в процессе шифрования не превышает нескольких миллисекунд. sky.provk.comstackoverflow.comvk.com
Это достигается за счёт оптимизации алгоритмов и использования современных методов, таких как режим CBC (Cipher Block Chaining), который позволяет обрабатывать данные частями.
eungenue
29.05.2025 21:38Главные шпионы в наше время - это устройства, на которых всё это замечательное «шифрование» происходит. Там и ОС закрытая, и постоянная неотключаемая связь с настоящим хозяином, и пароли все эти ваши. А так - ну, можно и пошифровать, батарейку и проц разогреть.
Vic1111
29.05.2025 21:38именно так, после дистанционного заражения смартфона не важно какое у вас крутое шифрование или какой меcседжер. Поэтому если смартфон то шифрование например голоса должно быть вынесено за пределы телефона, это блютузная например приставка.
Chupaka
29.05.2025 21:38А как пользователи-то ключами управляют? Например, передать публичный через сервер — ещё куда ни шло, а вот приватный после логина в приложение откуда берётся на устройстве, чтобы расшифровать все сообщения, отправленные, пока ты офлайн был?
Nexoic
29.05.2025 21:38Его сохранять надо в памяти после логина на клиенте и не сохранять в бд или где то если по честному делать
Chupaka
29.05.2025 21:38Я же о том и спрашиваю: если я залогинюсь на чистом устройстве, то все сообщения, которые мне отправляли до этого момента, я не получу, поскольку сгенерируется новый ключ, а они были отправлены старому?
supercat1337
29.05.2025 21:38Зависит от реализации. Может приватный ключ кто-то создаёт из пароля и соли.
Ekhidirov Автор
29.05.2025 21:38Обратите внимание, что приватный ключ дополнительно шифруется. В качестве примера его можно зашифровать паролем пользователя при регистрации аккаунта или любым другим ключевым словом, известным только вам. После шифрования приватный ключ сохраняется в базе данных. При авторизации клиенту передаётся только зашифрованный приватный ключ, который он расшифровывает с помощью пароля, введённого при авторизации. Затем ключ сохраняется на устройстве пользователя до окончания сессии авторизации. Но если пользователь забудет свой пароль от своей учетной записи, возникает проблема он больше не сможет расшифровать свои старые сообщения.
antirek
29.05.2025 21:38т.е. если у меня два устройства - то зашифрованный приватный ключ проходит через сервер. если расшифровывать с помощью пароля, то при смене/восстановлении пароля расшифровка невозможна. какие еще варианты сохранения безопасности приватного ключа?
Ekhidirov Автор
29.05.2025 21:38Да, всё верно если пароль утерян, восстановить сообщения уже не получится. Но это лишь один из возможных сценариев. Приватный ключ можно шифровать не только паролем пользователя, но и другими ключевыми словами. Например, это могут быть recovery codes, которые система случайным образом генерирует и затем использует для шифрования приватного ключа.
Допустим, сгенерировано 6 recovery codes тогда система создаёт 6 зашифрованных копий приватного ключа, каждая из которых связана с одним из этих кодов. Эти копии необходимо дополнительно хранить, например, в базе данных. При смене пароля пользователю стоит предлагать восстановить сообщения с помощью recovery codes и заранее обратить внимание на важность их сохранения ...
nin-jin
29.05.2025 21:38С тем же успехом можно и приватный ключ в открытом виде где-то "важно сохранять".
xidisidi
29.05.2025 21:38Смысл тогда заниматься шифрованием сообщений если приватные ключи где то будут храниться в открытом виде? Это будет не справедливо по отношению к пользователям которые будут думать что их сообщения шифруются енд ту енд. Если приватные ключи будут храниться в открытом виде значит кто то будет иметь возможность расшифровать любые сообщения вопрос возникает тогда кто? Админ, владелец хостинга, хакер, сотрудник который по ошибке слил базу и т д?
nin-jin
29.05.2025 21:38Вы не поняли, тут речь не про хранение в базе, а про хранение в надёжном месте у пользователя. Ну там распечатать и в сейф или типа того.
ckpunT
29.05.2025 21:38После прочтения Асимметричное шифрование и ECDH магия осталась магией из-за пропущенного процесса вычисления общего секретного ключа. Потому что
Голову можно ломать долго и безрезультатно, пока не поймем, что такое асимметричное шифрование и протоколы обмена ключами.
Если не погружаться в самые дебри, то:
У Алисы есть приватный ключ (a) и публичный ключа (A). Публичный ключ высчитывается из приватного ключа A = a x G
У Боба так же приватный (b) и публичный (B = b x G)
G – это базовая точка на эллиптической кривой, общая для всех. Алиса и Боб заранее договорились какую кривую будут использовать. За нас уже всё посчитано, точка описана в стандарте, просто берем её оттуда.
Алиса и Боб обменялись публичными ключами и каждый из них умножает свой приватный ключ на публичный ключ собеседника:
Алиса: a x B => a x (b x G) = ab x G
Боб: b x A => b x (a x G) = ba x G => ab x G
Вот и вся "магия" получения общего секретного ключа
cordova07
29.05.2025 21:38Это можно как то протестировать? Если честно ничего не понял)
ckpunT
29.05.2025 21:38более простого объяснения не знаю
from math import gcd from sympy import primefactors def is_primitive_root(g, p): if gcd(g, p) != 1: return False order = p - 1 for q in primefactors(order): if pow(g, order // q, p) == 1: return False return True def min_primitive_root(p): for g in range(2, p): if is_primitive_root(g, p): return g if __name__ == '__main__': sess_const = 23 # Большое простое число (в реальности 2048+ бит) pr = min_primitive_root(sess_const) # Первообразный корень по модулю sess_const a = 6 # Секретный ключ Алисы A = pow(pr, a, sess_const) # Публичный ключ Алисы (отправляется Бобу). b = 15 # Секретный ключ Боба B = pow(pr, b, sess_const) # Публичный ключ Боба (отправляется Алисе) K_alice = pow(B, a, sess_const) K_bob = pow(A, b, sess_const) print(f"Публичные параметры: p = {sess_const}, g = {pr}") print(f"Алиса: приватный a = {a}, публичный A = {A}") print(f"Боб: приватный b = {b}, публичный B = {B}") print(f"Общий секретный ключ у Алисы: {K_alice}") print(f"Общий секретный ключ у Боба: {K_bob}")
Здесь есть подробное объяснение как это работает, но без подготовки это скорее всего не осилить.
Тут можно закончить словами автора:Всё, что вам нужно понять на данном этапе, – это то, что технология работает
supercat1337
Спасибо, хорошая статья! Можно еще добавить, что web crypto доступен и в принципе рекомендован к использованию в воркерах, чтобы не замедлять ui во время работы с криптографией. На маленьких сообщениях это незаметно, а вот при работе с большими файлами там другая история.
nin-jin
Он и так в отдельном потоке исполняется, потому функции и асинхронные.
Krokochik
Пул потоков-то ограничен. В реальности у вас ui будет из-за subtle стоять в очереди, если не вынести в воркер
nin-jin
Вы бы проверили сперва, прежде чем так уверенно говорить про реальность.
Krokochik
У меня в приложении ui по 3 секунды висел из-за сложного pbkdf. Я научен)
nin-jin
Видимо вы в рендерере влепили await, из-за чего он остановился в ожидании ответа от крипто-треда. И не разобравшись подумали, что криптография исполняется в ui-треде.
Krokochik
Как я уже сказал, пул потоков ограничен. Почитайте что-нибудь по теме, почему криптографию выносят в воркеры
nin-jin
Давайте уже свои ссылки по теме, не томите общественность.
Ekhidirov Автор
Тут я с вами соглашусь пул потоков действительно ограничен. В идеале всю тяжёлую логику стоит перенести в веб воркеры, чтобы основной поток был занят только обработкой пользовательского ввода, включая клики. Это особенно заметно на бюджетных мобильных устройствах за $100, где часто наблюдаются микролаги и прочие задержки. Воркеры могут помочь с этой проблемой.
Что касается WebCrypto, он выполняется вне основного потока и никак на него не влияет. Таким образом, если вернуться к теме статьи, шифрование и дешифрование именно сообщений обходится недорого даже для слабых устройств.
Krokochik
Я писал больше про тяжелые и долгие операции, которые специально замедленны, вроде pbkdf. Расшифровка/шифрование, когда необходимости в усложнении нет, т.к. токены итак затруднительно подобрать, даже при большом объеме данных не должны вызывать проблем. Максимум подготовка данных, но это на совсем слабых устройствах