Добавил arm64 в свою реализацию шифра “Кузнечик” (ГОСТ Р 34.12-2015) на ассемблере для Go. Новые функции – для платформы arm64 со 128-битной арифметикой. Попутно немного изменил состав библиотеки, так что название теперь новое (см. исходники ниже). Раньше ассемблер был только для amd64 с AVX, на остальных платформах – компилировалось с “заглушкой”, в которой операции шифра реализованы просто на конструкциях Go – ассемблер, понятно, быстрее.

ARM – весьма и весьма распространённая архитектура, а использование ассемблера, как обычно, даёт прирост производительности во много раз, если сравнивать с “обычным” Go-шным вариантом (тоже оптимизированным, конечно). Однако я всё проверял на Raspberry Pi4 (RPi4), потому что другого варианта ARM-аппаратуры под рукой сейчас не оказалось (а техники Apple у меня вообще нет). Зато на Linux/arm64 в RPi4 – прирост более чем в двадцать раз. Было: примерно мегабайт в секунду для “обычной” реализации. Стало: около 23 мегабайтов в секунду для ассемблерной. Думаю, что должно работать и на других ARM-платформах, и гораздо быстрее, чем на RPi4, но пока не проверял: если кто-то использует, и обнаружит существенные несовместимости (см. ниже) – пишите, пожалуйста, в комментарии здесь или пишите почтой. Вообще, написанный ARM-код ещё есть куда оптимизировать: например, в arm64 имеется “векторная” инструкция, реализующая XOR сразу для трёх входных регистров, что тут очень полезно, но эта инструкция недоступна в процессоре RPi4 (Cortex-A72/BCM2711).

Новая версия кода “Кузнечика”, так как отличается от предыдущей, называется GOSThp. Исходный код публикую ниже. Возможно, потом оформлю в виде полноценного модуля для Golang и выложу копию на Github, а возможно – не оформлю и не выложу. Но использовать в составе проекта на Go этот пакет нетрудно: достаточно скопировать директорию gosthp (см. ещё детали ниже). Работает со старыми версиями Go (проверено для 1.7).

Файлы:

gosthp.tar.gz – всё вместе в одном архиве.
SHA-256: b3f860f15d43814db2f2becfb4c72272de6d3b49f9b0dec70712438af0a6e085
(Update, 20/01/2024: архив перезагружен – изменено описание платформ в +build, даты в комментариях, и даты создания файлов; код – без изменений.)

Внутри архива:

cmd/
	| gostrun.go		- файл с примерами и тестами.
gosthp/	- это сама библиотека.
	| docipher_amd64.go	- объявления функций для amd64.
	| docipher_amd64.s	- код на ассемблере для amd64.
	| docipher_arm64.go	- объявления функций для arm64.
	| docipher_arm64.s	- код на ассемблере для arm64.
	| docipher.go		- универсальная реализация для прочих платформ (см. +build внутри: могут быть расхождения).
	| gosthp.go		- основной модуль.
go.mod
README

Основное дополнение – docipher_arm64.s, собственно, реализация шифра для архитектуры arm64/AArch64.

Как обычно, прилагается демонстрационный код – gostrun.go. Если развернуть упомянутый выше архив в директорию, то собрать gostrun можно так:

$ cd cmd
$ go build -o ~/my-builds/gostrun gostrun.go

Go самостоятельно выберет нужные файлы в соответствии с целевой платформой. Внутри исходников – много комментариев (англ.).
Саму библиотеку можно использовать в среде Go в составе других проектов простым копированием gosthp/, разместив её либо в директорию сборки, либо в общую директорию для пакетов.

Ассемблерная реализация для ARM от x86 отличается в некоторых деталях, не алгоритмических: во-первых, очевидно, другие инструкции; во-вторых, используется больше регистров и основные операции XOR собраны в общий блок; в-третьих, изменён способ вычисления смещений для обращения к таблице значений.

Screenshot

Как обычно, основная оптимизация – использование предвычисленной таблицы. На ассемблере написаны только низкоуровневые операции шифра, то есть, преобразование блока с таблицей и раундовыми ключами. Библиотека позволяет создать экземпляр объекта, который реализует интерфейсы для доступа к шифру. Таблица, ускоряющая операции, общая для всех экземпляров, эта таблица создаётся при инициализации модуля, а потом используется процедурами, реализующими шифр, только для чтения. Строго говоря, таблиц там несколько: есть “обратная” таблица для расшифрования и несколько вспомогательных (см. исходный код). Так как значения в таблицах не зависят от конкретного ключа, то их использование не требует повторного вычисления значений. В принципе, можно было бы просто записать эти таблицы в код в виде статического массива, так часто делают, но можно и генерировать программно, что делает код менее громоздким, а на производительность никак не влияет.

Базовые процедуры шифра (зашифрование/расшифрование) используют набор ключей раундов, которые создаются при инициализации конкретного экземпляра cipher – раундовые ключи как раз зависят от ключа шифрования, поэтому записываются в конкретный экземпляр структуры, в которой используются при повторных вызовах. То есть, разворачивание ключа тут тоже производится только один раз, при создании экземпляра cipher.

Что касается совместимости. И amd64, и arm64 – достаточно широкие понятия. В этой реализации шифра “Кузнечик” используются регистры и команды для разрядности 128-бит, которая соответствует разрядности шифра – в этом, собственно, смысл. Уже использование команд “длинной” арифметики из SSE4.1 для amd64 означает, что некоторые старые процессоры, полностью попадающие в amd64, не смогут, тем не менее, исполнять скомпилированный код, поскольку не поддерживают нужных команд. То же самое относится и к arm64 (например, выше я уже упомянул, что не использую “трёхрегистровую” команду XOR по этой же причине). Данная библиотека поддержку команд не проверяет, так что, вообще говоря, это нужно отслеживать отдельно, используя механизмы детектирования свойств аппаратуры, и, соответственно, либо заменять вызываемые функции, либо выводить предупреждение пользователю.



Комментарии (7) »

В алгоритме ECDSA есть число, обычно обозначаемое k, которое используется при вычислении значения подписи, а именно – k определяет параметр r из пары (r,s) подписи. Значение r из этой пары необходимо для того, чтобы сошлась формула проверки. В исходном алгоритме k предлагается выбирать случайным образом (но без повторов, и держать в секрете). Дело в том, что если третья сторона знает k, то она может элементарным способом вычислить секретный ключ по сообщению с подписью; повторное использование k для разных сообщений, при прочих равных, так же приводит к раскрытию секретного ключа.

Можно встретить мнение, что такая особенность заложена в ECDSA специально (оставлено за скобками то, что такой подход использовался и в других, предшествующих ECDSA, криптосистемах). Действительно, если, например, k вычисляется по некоторому алгоритму генерирования псевдослучайных чисел “с секретом”, то если третьей стороне известны скрытые особенности данного алгоритма, эта сторона может раскрыть секретный ключ ECDSA, быстро подобрав k под открытое значение r, которое можно взять из подписи. (Это хрестоматийный теоретический подход к созданию бэкдоров методом “алгебраического разбиения”.)

Вообще, на этом направлении весьма легко ошибиться в программном коде, без всякого бэкдора. Либо подведёт аппаратура, обеспечивающая выдачу случайных значений. Либо вмешается тот или иной гипервизор – сейчас повсеместно используется виртуализация для размещения программного кода, вычисляющего ECDSA-подписи, с этим сложно что-то поделать, как и с тем, что инженеры DevOps обожают делать “снапшоты” виртуальных машин, а потом их восстанавливать (так себе решение) или множить (ещё хуже). Заметьте, кстати, что всё это полностью применимо и к современной ГОСТ-подписи – там математически эквивалентная схема.

Так что проблем с псевдослучайным параметром в ECDSA много. На практике, в алгоритм вычисления k так или иначе подмешивают дополнительные значения, не ограничиваясь лишь выдачей генератора псевдослучайных чисел. Это полумера. Однако есть и полностью детерминированный вариант (“deterministic ECDSA“), в котором значение k вычисляется для конкретного сочетания сообщения и секретного ключа (например, такой алгоритм поддерживается свежим OpenSSL 3.2).

Практика использования детерминированного варианта сопряжена с ещё одним занятным моментом. Если сообщение подписывается обычной ECDSA (или ГОСТ-подписью), то значение подписи будет каждый раз разным, даже для одного сообщения и одного подписывающего ключа. То есть, значение подписи псевдослучайное, и этот факт вполне может использоваться приложениями (хотя, вообще говоря, не должен бы). Соответственно, “фиксирование” значения подписи в таком приложении может что-то сломать. Но детерминированный вариант, конечно, всё равно лучше.



Комментировать »

На OpenNET пишут про RFC для децентрализованной системы имён GNS:

“Использование Curve25519 воспринимается некоторыми как весьма странный шаг, так как для ECDSA применяют другие типы эллиптических кривых, а в паре с Curve25519 обычно используют алгоритм цифровых подписей Ed25519, более современный, более безопасный и более быстрый, чем ECDSA. С точки зрения криптостойкости в том числе вызывает сомнение выбор размера закрытого ключа – 32 байта вместо 64 байт”

В GNS, действительно, используют ECDSA на кривой Curve25519. Это может, конечно, показаться странным. Однако алгоритм ECDSA работает в группе точек и вообще не зависит от выбора кривой (да, даже про “суперсингулярные кривые” тут есть занятные уточнения). Поэтому ничто не мешает взять Curve25519 вместо, например, более привычной P-256. Какие-то сугубо математические свойства Curve25519, типа наличия кофактора и т.п., вовсе и не являются необычными – такие кривые вполне себе подходят и для ECDSA. Так что, если нет доверия той же P-256, но нужен именно алгоритм ECDSA – можно взять Curve25519. Использование же Ed25519 в данном протоколе невозможно из-за особенностей преобразования ключей, о чём, собственно, сказано в RFC. Насчёт “более быстрого” алгоритма Ed25519 – это, в основном, определяется как раз параметрами кривой (поле и т.д.).

Что касается странного дополнения про 32-байтовый и 64-байтовый ключи: тут, наверное, что-то перепуталось на каком-то этапе пересказывания. В Ed25519 секретный ключ – 32-байтовый. И в ECDSA на P-256 (например) – тоже 32-байтовый. Потому что разрядность в 256 бит (32 байта) делает бессмысленным использование секретных ключей большей длины: всё равно значение сократится. А 64 байта – это общий размер подписи, а не ключа.

Можно предположить, что тут ещё сыграло следующее популярное заблуждение, которое нередко наблюдается в отношении SSH-ключей: многие считают, что, например, поскольку открытый ключ Ed25519 короче, чем ECDSA на той же P-256, он поэтому и менее “криптостойкий”. Действительно, для ECDSA/P-256 открытый ключ обычно записывается в 64 байта (иногда чуть меньше, иногда – чуть больше, зависит от кодирования), а в Ed25519 – только в половину, в 32 байта. Однако эти 64 байта ECDSA математически эквивалентны 32 байтам, там половина байтов приносит только один бит дополнительно: открытый ключ представляет собой точку на кривой, у точки – две координаты (X,Y), каждая по 32 байта, и вот полная форма записи ключа ECDSA подразумевает указание объединения X и Y, откуда и получается 64 байта; однако можно указывать только одну координату (X), а вторую (Y) – вычислять из уравнения кривой при необходимости. В такой схеме, для ECDSA, потребуется сохранить дополнительно знак, это один бит, и получается тоже около 32 байтов для записи открытого ключа. А вот в Ed25519 алгоритм предписывает всегда использовать только одну координату (ну, строго говоря, там есть ещё некоторые преобразования, но здесь они не важны). То есть, математически эти ключи совпадают по представлению, отличие тут чисто прикладное, поэтому дополнительные 32 байта записи для ECDSA не делают даже открытый ключ в два раза длиннее “криптографически” – он всё равно 256-битный (по разрядности кривой).



Комментировать »

Кстати, если вдруг придумают алгоритм быстрой факторизации больших полупростых чисел, сломав тем самым RSA, то, конечно, “популярной мотивации” для создания универсального квантового компьютера станет сильно меньше: на этом направлении всегда пишут про алгоритм Шора. А вот с необходимостью перехода на постквантовые криптосистемы, в этом случае, есть интересные особенности.

Так, обнаружение алгоритма быстрой факторизации, естественно, легко может грозить тем, что попутно обнаружится и способ быстро сломать задачи типа привычного эллиптического варианта Диффи-Хеллмана (читай – ECDSA). Однако только из этого не следует автоматически вывод, что переходить требуется именно на постквантовые криптосистемы. Вот тут и начинаются особенности. Во-первых, постквантовые криптосистемы уже разработаны, уже имеют какую-то практическую “классическую” стойкость, уже реализованы, – так что их быстрее внедрить; во-вторых, задачи, на которых базируются те или иные постквантовые криптосистемы, имеют гораздо больше шансов сохранить известную сложность после обнаружения каких-то принципиально новых методов быстрой факторизации – это, конечно, тоже не гарантируется, однако для ускорения факторизации/логарифмирования до сих пор использовались математические конструкции, которые, в общем-то, стоят и за многими постквантовыми алгоритмами.



Комментировать »

Представьте, что некоторый протокол туннелирования в Интернете устроен следующим образом: в качестве базового транспорта используется UDP (то есть, отсутствуют сессии транспортного уровня, тут это важно); применена схема “клиент-сервер”, использующая двухстороннюю аутентификацию с общим секретом, а процесс аутентификации тоже полностью зашифрован (пробное расшифрование и т.д.); используются различные IP для серверов – точек входа, а также динамически изменяемые номера портов; полезные данные тоже полностью зашифрованы – отсутствуют какие-либо открытые заголовки (кроме UDP), отсутствует фиксированная структура пакета, пакеты имеют разную длину, а адреса/номера портов изменяются в ходе соединения. Подобные протоколы есть, но в этой заметке речь о том, почему именно такой подход создаёт трудности в обнаружении и блокировании соединений.

Протокол медленный, не предназначен для создания стабильных и широких каналов, но зато он скрытый, полностью “мутирующий” и “размытый” (возможно, кто-то из читателей помнит, как в своё время появились первые развитые “полиморфные” компьютерные вирусы, содержавшие зашифрованное тело и генерируемый псевдослучайным образом “распаковщик” – теоретически, никаких статических сигнатур). Описанный протокол как раз внешне выглядит как случайный поток случайных UDP-пакетов, в котором не видно никаких сессий и контекста, не определяется внутренний статус, а общим является только адрес возможного клиента, так как он фигурирует и в отправителях, и в получателях, а вот уже входной и выходной серверные IP-адреса могут изменяться; это особенно занятно, если использовать IPv6 (за скобками оставлено преодоление NAT и некоторые другие сетевые особенности).

Теперь представьте, что некоторая система DPI должна блокировать соединения и прерывать сессии, используя в качестве флага тип протокола. Но у “размытого” протокола, описанного в предыдущем абзаце, нет устойчивых признаков, позволяющих уверенно определить его тип. Конечно, можно сказать, что такой признак всё же есть, просто он “несобственный” – исследуемый протокол “не похож ни на что известное”. Но для такого метода классификации нужно не только составить “белый список” протоколов, но ещё и так запрограммировать систему, чтобы она и классифицировала все протоколы, а это исключает всякую гибкость в пропуске трафика. Скажем, вот обнаружен UDP-пакет, который пока что не соответствует никакому контексту из уже созданных узлом DPI, инспектирующим трафик: если пакет относится к этапу установления соединения в рамках “размытого” протокола и если этот пакет не пропустить, то такое соединение не состоится. Но как определить, что это не пакет, выпавший из другой сессии “разрешённого” протокола (типа DTLS, предположим)?

Надёжное определение контекста становится затруднительным на достаточных объёмах трафика: у допустимых протоколов есть внутренние открытые заголовки, но они короткие и, в теории, могли просто совпасть, поэтому обязательно нужно взвешивать дополнительные параметры по нескольким пакетам – длину, адреса, номера портов. Но чтобы взвешивать несколько пакетов, для них придётся создать некоторую очередь измерений. А как сделать такую очередь эффективной, если туда предлагается складывать всё, что не получилось разобрать? Кроме того, задержка пакета “размытого” протокола приводит к тому, что другие пакеты просто не поступают – не из чего собирать полезный контекст в принципе. Если один “размытый” пакет пропустить, то получится, что и обмен данными случился, и тут уже вовсе не обязательно, что пакет относился к процессу аутентификации – он мог и нести полезные данные в рамках скрытой сессии. Более того, если некоторые пакеты проходят, то это означает, что сам “размытый” протокол успешно работает, преодолевая блокирующие узлы, потому что одно из заявленных свойств протокола – его внутренний статус нельзя определить даже по нескольким пакетам.

Вообще, классификация подобных протоколов с формированием контекста с целью распознавания – та ещё задача. В упрощённом виде можно представить, что классификатор работает с некоторым деревом состояний, переходы по которому определяются наличием видимой для DPI активной сессии между узлами (то, что в распространённых брандмауэрах называется Established connection), наличием ответных (парных) сообщений (это детектирование типовых “хендшейков” вида “запрос-ответ-подтверждение” на начальной стадии), соответствием заголовков транспортных пакетов и параметров адресации (адреса, номера портов), наличием заданных сигнатур и средним объёмом переданных данных. Понятно, что только лишь поиск сигнатур в конкретном пакете – тут заведомо не работает: нужно именно сопровождение состояний во времени. При этом классификация пакетов “размытого” протокола требует максимального задействования вычислительных ресурсов, нужных для расчёта эволюции только что описанного дерева – ведь каждый пакет требуется обработать, провести по функциям проверки, а потом положить информацию в буфер, чтобы позже проверить, не придёт ли какой-то ещё пакет, который можно привязать к уже полученному. Так что это всё скорее теоретические рассуждения, чем доступные на практике методы.

(Я уже писал о полностью зашифрованных протоколах раньше, в том числе, про распределение ключей и влияние TLS.)



Комментарии (3) »

Открытый ключ Kyber768 (постквантовой криптосистемы) – это 1184 байта, то есть, 9472 бита. Это заметно больше, чем занимает практический 8-килобитный ключ RSA. Для взлома такого RSA-ключа на квантовом компьютере (весьма гипотетическом), требуется количество логических кубитов в разных регистрах не меньше 24576 == 8K * 3. Но это теоретический минимум для логических кубитов, без учёта квантовых схем и возможной физической реальности. Поскольку технологические принципы реализации алгоритма Шора пока что не известны, оценки того, сколько физических кубитов и квантовых элементов потребуется для подобной разрядности, затруднены и сильно различаются, но нетрудно получить оценку в миллион (и более). И этот миллион квантовых эллементов, к тому же, может не сработать.

Естественно, это не отменяет того, что Kyber768 тут выглядит лучше, если обладает классической стойкостью. Кроме того, RSA – устаревшая криптосистема, а для коротких, относительно RSA, ключей ECDSA (например) – и кубитов потребуется поменьше, при этом прямо “растягивать” ту же ECDSA на несколько килобит – так себе идея.



Комментировать »

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



Комментировать »

Предполагается, что постквантовые криптосистемы – это защита от взлома на квантовом компьютере. На гипотетическом квантовом компьютере, который может реализовать соответствующие алгоритмы – алгоритм Шора, прежде всего. Конечно, современный уровень “хайпа” вокруг квантовых компьютеров уступает уровню “хайпа” вокруг “искусственного интеллекта”, тем не менее, квантовых компьютеров, подходящих для атак на используемые сейчас криптосистемы, ещё никто не показал. И даже ничего близко похожего – не показали. Но если почитать, например, статью про квантовые вычисления даже в англоязычной “Википедии”, то там почему-то уверенно обсуждаются “практические особенности”. Но до “практики” же ещё очень далеко. Пока что даже исследовательские алгоритмы, призванные показать “квантовое превосходство”, требуют создания специальных задач, которые структурно оптимизированы не в направлении вычислительной полезности, а в направлении использования свойств, потенциально доступных на имеющихся сейчас квантовых устройствах (см. boson sampling). Это естественно, весьма логично для этапа теоретических исследований на экспериментальном оборудовании, но не относится к практическому применению универсальных компьютеров.

В популярных изложениях нередко сильно искажают ситуацию (а иногда – искажают и не в совсем популярных: см. историю про “голографическую кротовую нору”), заявляя, что алгоритм Шора уже был успешно реализован на таких-то и таких-то конфигурациях. При этом для алгоритма Шора ключевое значение имеет не “суперпозиция состояний”, про которую всё время рассказывают, а реализация квантового преобразования Фурье, потому что именно в нём состоит содержательная часть – алгоритм должен работать потому, что схемы преобразования Фурье позволяют, в теории, определить период функции, заданной на значениях квантовых регистров. Однако в экспериментах именно эту часть (преобразование Фурье) существенно упрощают или вообще исключают, так как нет доступных экспериментальных квантовых схем, подходящих для практической реализации. На малых разрядностях (несколько битов/кубитов) преобразование Фурье для алгоритма Шора вообще не имеет вычислительного смысла, поскольку в принципе нельзя увидеть “длинных” периодов. Не исключено, что в случае “коррекции ошибок” на дополнительных схемах – преобразование Фурье совсем не будет работать для отыскания периода из-за того, что алгоритм-то, по предназначению, целочисленный. И это если оставить за скобками то, что создание гипотетического квантового компьютера большой разрядности напрямую затрагивает основания современной физики, поскольку именно такой квантовый компьютер с необходимостью попадает на границу между “квантовым (микро)миром” и “неквантовым (макро)миром”, которая совсем не ясна, вокруг которой строятся разные интерпретации.

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



Комментарии (1) »

В продолжение недавней записки про X25519Kyber768 на TLS-сервере – подробности встраивания данной гибридной криптосистемы в схему работы TLS 1.3.

1. Как нетрудно догадаться, X25519Kyber768 состоит из X25519 и Kyber768, поэтому криптосистема и гибридная. X25519 – это хорошо известный вариант протокола Диффи-Хеллмана (DH), здесь используется без изменений, обычным образом (см. кстати, заметку с задачей про 2^255 – 19). Kyber768 – схема KEM (инкапсуляции ключа), построенная на криптосистеме Kyber с “постквантовой стойкостью”. Эта криптосистема реализует зашифрование с открытым ключом (важный момент).

2. В TLS рассматриваемая криптосистема используется для получения симметричного сеансового секрета. Открытый ключ передаётся клиентом в составе ClientHello, в расширении key_share, а ответная часть сервером – в ServerHello, key_share. В логике сообщений тут ничего не меняется.

3. Изменяется часть внутри key_share, соответствующая X25519Kyber768 – клиент передаёт результат конкатенации байтов, составляющих клиентскую часть DH X25519 и открытый ключ клиента Kyber768. Эти данные имеют фиксированную длину, определяемую алгоритмами: 32 начальных байта для X25519 и 1184 для Kyber768, 1216 байтов всего. На сервере данные разделяются (просто, по длине) и используются для обеих криптосистем. А именно: для X25519 сервер вычисляет общий секрет DH и серверную открытую часть DH (B), так же, как это делалось бы в случае отдельной криптосистемы X25519; для Kyber768 – сервер генерирует общий секрет и оборачивает его в KEM (то есть, зашифровывает исходное секретное значение, используя открытый ключ Kyber, присланный клиентом – тем самые 1184 байта). Два секрета сервер объединяет в один массив – здесь, опять же, простая конкатенация: BaseSecret = x25519[] + Kyber_Shared_Secret[]. Обратите внимание на важное техническое отличие: для X25519 общий секрет, на сервере, это результат умножения открытой части DH клиента (A) на секретный скаляр сервера d: s = d*A; а для Kyber – сервер выбирает исходное значение, которое отправляет клиенту в зашифрованном виде (очень похоже на устаревшую схему с RSA-шифрованием в TLS, но устроенную наоборот). При этом внутри KEM Kyber для вычисления секрета по исходному значению используется отдельная функция (KDF), подмешивающая ещё и значение открытого ключа, это необходимый шаг, но, с точки зрения логики получения секрета, это не так важно. Секрет, генерируемый в рамках Kyber768 в TLS – это тоже 32 байта (256 бит). После завершения данного этапа – сервер получил общий симметричный секрет, представляющий собой объединение выдачи двух алгоритмов: 32 байта и 32 байта. Также сервер получил открытую часть DH и зашифрованный Kyber симметричный секрет (это только часть, предназначенная для Kyber, результат X25519 сюда не попадает).

4. Сервер формирует ответное расширение key_share, присоединяя к 32 байтам открытой части DH X25519 байты шифротекста с симметричным секретом, который зашифрован Kyber – длина шифротекста 1088 байтов, всего 1120 байтов. Ответное key_share сервер отправляет клиенту в открытой части сообщений, в ServerHello, после чего генерирует на основе общего секрета набор симметричных сессионных ключей и переходит к зашифрованному обмену данными.

5. Клиент, получив key_share X25519Kyber768, разделяет данные на открытую часть обмена DH X25519 (B) и шифротекст Kyber768. По значению B DH клиент вычисляет общий секрет DH X25519 (здесь – 32 байта), который совпадает с серверным. Используя секретный ключ, клиент расшифровывает шифротекст и вычисляет общий секрет Kyber. Оба полученных значения объединяются, результат должен совпасть с серверным. (Тут слово “должен” использовано потому, что в Kyber, к сожалению, есть вероятностный элемент: так как это схема, концептуально происходящая из кодов с коррекцией ошибок, то имеется очень небольшая вероятность, что “ошибка” всё же останется, а клиент и сервер получат разные значения секрета.) На основе объединённого секрета клиент вычисляет набор симметричных ключей и может проверить подлинность и расшифровать следующие сообщения сервера.

Таким образом, Kyber простым и понятным способом добавляет 256 бит “постквантовой стойкости” к исходному симметричному секрету TLS-сессии, какие-то другие параметры – не изменяются.



Комментировать »

Кстати, индекс 0x6399, который позволяет отличить ключи криптосистемы X25519Kyber768Draft00 в TLS-сообщениях, внесён в соответствующий реестр номеров IANA, в раздел под именем TLS Supported Groups. Название раздела – ещё один пример исторически сложившихся особенностей спецификаций: понятно, что X25519Kyber768 – никакая ни группа, а сразу две криптосистемы, построенные на различном математическом аппарате (но, естественно, группы нетрудно найти и в X25519, и в Kyber768). Изначально, упомянутый раздел параметров TLS содержал индексы именованных эллиптических кривых, то есть, сочетаний из параметров, определяющих конкретную кривую в прикладном смысле. Но так как для криптосистем существенны были именно операции в группе, а кроме эллиптических кривых ещё использовались (и используются) мультипликативные группы колец вычетов, то раздел, довольно давно, переименовали в “Поддерживаемые группы”, теперь туда записывают наборы криптосистем.



Комментировать »

Всё же я пока восстановил свой экспериментальный сервер TLS 1.3 – tls13.1d.pw (некоторое время назад я писал, что собираюсь его совсем отключить). А чтобы восстановление выглядело поинтереснее, я реализовал на сервере поддержку постквантовой схемы получения общего секрета (KEM) X25519Kyber768. TLS с Kyber768 там реализован вручную, но я, впрочем, использовал криптопримитивы из удобной библиотеки Cloudflare.

Криптосистему с постквантовой схемой KEM Kyber768 в конце августа Google внедрил в браузер Chrome (в порядке эксперимента), так что можете проверить – на сервере у X25519Kyber768 повышен приоритет, поэтому, при наличии соответствующего открытого ключа в сообщении клиента, выбираться она должна довольно часто.

Вообще, открытый блок клиентского KeyShare в X25519Kyber768 весит аж 1216 байтов (32 + 1184, потому что это ключ X25519, 32 байта, плюс ключ постквантовой части Kyber768, который большой). Тем не менее, я всё же пока что сделал вывод этого ключа без сокращений, что, возможно, выглядит тяжеловато, но видно будет только в браузере с поддержкой данной криптосистемы. (Дополнение, 12/09/2023: технические подробности об использовании криптосистемы.)

Поддержка есть только в самых свежих версиях Chrome (>=116), а включать её нужно через флаги: chrome://flags, набрать “TLS13” в поиске, флаг называется “TLS 1.3 hybridized Kyber support”.

Screenshot with TLS 1.3 Kyber768

(Не знаю, будет ли у меня возможность поддерживать сервер далее, так что в какой-то момент он может отключиться, теперь уже даже и без предупреждения, но посмотрим; ошибки подключения, естественно, могут быть и по другим причинам – это, всё ж, экспериментальный сервер.)



Комментировать »