Finger & AIly
AIly AIly
Привет, я тут поглядывала свежие исследования по алгоритмам растяжения ключей — сравнивал ты Argon2 и scrypt с точки зрения устойчивости к атакам по памяти и задержкам? Интересно, как бы ты оптимизировал хранилище паролей, чтобы было и быстро, и устойчиво к побочным каналам.
Finger Finger
Аргон2ид обычно лучший выбор, если тебе нужен хороший баланс между потреблением памяти и задержкой. Он позволяет точно установить размер памяти, время выполнения и степень параллельности – всё в одном вызове, да ещё и алгоритм разработан так, чтобы избежать утечек по времени доступа к кэшу. Scrypt тоже требует много памяти, но у него фиксированная структура с "солью", что усложняет независимую настройку задержки от объема памяти. Для хранилища паролей я бы выбрал Argon2id: установил бы объем памяти минимум в 512 мегабайт на ноутбуке, время выполнения – 3–5 раундов, и параллельность – 2 или 4, в зависимости от процессора. Затем оберни хеш в функцию сравнения с постоянным временем выполнения, сразу обнуляй буферы и, если ты беспокоишься о побочных каналах, вынеси весь код вывода ключей в отдельный модуль ядра. Это обеспечит хорошую скорость на современном оборудовании, при этом снизив поверхность для атак.
AIly AIly
Звучит неплохо – Argon2id с 512 мегабайт и несколько раундов – хороший старт. Я быстро проведу бенчмарк на своей тестовой машине, а потом забронирую на завтра на десять минут, чтобы подкрутить параллелизм. Еще напишу процедуру очистки буфера с нулевым размером; чем она детерминированнее, тем меньше простора для маневра у злоумышленника. Скажи, тебе нужна точная таблица параметров или ты тестируешь на другой линейке процессоров?
Finger Finger
Отлично. Десяти минут хватит, чтобы не распыляться, но и успеть учесть все нюансы. Пришли таблицу параметров, как будешь готова, я свежу ее с целевым уровнем ЦП – посмотрю кривые памяти и времени. Только убедись, что рутина очистки работает за постоянное время, я проверю логику, как только увижу.
AIly AIly
Конечно, вот таблица: - Память: 512 МБ - Время выполнения: 4 итерации - Параллелизм: 2 - Размер соли: 16 байт - Длина вывода: 32 байта Вся очистка выполняется в одном линейном цикле, который обнуляет буфер без разветвлений. Отправлю исходный код вместе с таблицей. Скажи, нужно ли подстраивать константы под твой уровень производительности процессора?
Finger Finger
Выглядит надёжно. 512 мегабайт – вполне достаточно, четырех проходов хватит, чтобы уложиться в целевые 10-15 миллисекунд на процессоре среднего уровня, а параллелизм в 2 сдерживает всплески нагрузки на графический процессор. Только убедись, что источник времени для таймера не подвержен утечкам. Постоянно-временное сравнение вычисленного ключа решит остальное. Если ты на более новом четырехъядерном узле, можешь увеличить параллелизм до 4 для небольшой прибавки в скорости, но сохраняй объем памяти прежним, чтобы оставаться в той же зоне риска. Если нет, присылай исходный код, я быстро пробегусь по нему.
AIly AIly
Поняла, вот фрагмент для очистки цикла и сравнения: ```c void zero_buf(void *p, size_t n) { volatile unsigned char *ptr = p; while (n--) *ptr++ = 0; } int cmp_const(const void *a, const void *b, size_t n) { const unsigned char *x = a, *y = b; unsigned char diff = 0; while (n--) diff |= *x++ ^ *y++; return diff == 0; } ``` Таймер использует `clock_gettime(CLOCK_MONOTONIC_RAW)` – без разрешения на уровне процесса. Отправляю, дай знать, как обертка для модуля ядра работает у тебя. Мы все сделали как надо.
Finger Finger
Обработка с использованием указателя, который может изменяться, не дает компилятору оптимизировать запись и сравнение, маскируя любые преждевременные отклонения во времени. `clock_gettime(CLOCK_MONOTONIC_RAW)` – отличный выбор для необработанной базовой линии. Главное, чтобы обертка ядра сохраняла постоянную семантику времени и не выставляла никаких битов статуса, которые можно измерить. Если подключишь и получишь одинаковое время, около 10–15 миллисекунд, на целевых процессорах – все в порядке.