Речь о протоколе, который скрывает метаинформацию о самом факте обмена сообщениями. Какими свойствами должен обладать такой протокол? Можно ли что-то подобное вообще реализовать на практике? Прежде чем такие вопросы более или менее содержательно формулировать, нужно, конечно, выбрать модель угроз.

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

Скрытый протокол, по условиям задачи, должен работать на базе распределённой сети и не требовать строгого центрального управления всеми узлами при обмене сообщениями. Это обусловлено риском компрометации такого “центра управления”, а также тем, что центральный вариант гораздо более уязвим к сегментации сети (да, есть варианты с автоматическим выбором нового “центра” и так далее, но мы пока просто примем, что протокол использует распределённую сеть).

Сразу же возникает вопрос о транспорте данных (может быть, этот транспорт – TCP?), но так как мы практически сразу столкнёмся с необходимостью стеганографии и маскировки узлов, то с транспортом всё окажется не так просто, поэтому конкретный протокол в условия задачи не включаем, оговорим только, что “какой-то транспорт” между “какими-то узлами” должен быть доступен.

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

Итак, какими свойствами и механизмами должен обладать скрытый протокол в таких (весьма жёстких) условиях?

1.

Очевидно, сообщения должны быть зашифрованы, а также – аутентифицированы. Это самая простая часть: стороны применяют симметричные ключи, распределённые тем или иным способом, и стойкий шифр с механизмом проверки подлинности. Зашифровать сообщения нужно не только и не столько для того, чтобы скрыть “полезную нагрузку”, сколько с целью удаления из потока статистически значимых признаков, позволяющих распознать факт обмена сообщениями.

2.

Для сокрытия трафика необходимо использовать стеганографию. В принятой модели угроз, если один из узлов просто передаёт зашифрованное сообщение вне прочих сеансов, то этот факт тут же оказывается обнаружен. Стеганографический канал маскирует сеанс обмена сообщениями под трафик другого типа. Это означает, что протокол не может прямо использовать привычные виды транспорта, а должен подразумевать наличие дополнительного трафика. Простой пример: сообщения могут быть скрыты внутри текстов веб-страниц и веб-форм, в качестве базового трафика используется имитация работы с веб-сайтом. В данном случае, веб-трафик может передаваться по TCP или UDP, с использованием TLS/HTTPS или QUIC, экзотические варианты вместо веб-трафика используют непосредственно TLS, или даже служебные заголовки IP-пакетов и DNS, всё это не так важно для стеганографии. Важны достаточный объём данных и наличие симметричных ключей, позволяющих организовать стойкий стеганографический канал. При наличии ключей – такой канал организовать всегда можно. (Ключи используются для задания псевдослучайной последовательности, которая необходима для работы механизма встраивания скрытых данных в поток трафика.)

3.

Так как третья сторона может подменять узлы, протокол должен предусматривать аутентификацию узлов. При этом процесс аутентификации оказывается сложным: узел не может просто так назваться узлом, использующим скрытый протокол, да ещё и подтвердить это подписью – подобное решение раскрывает всю секретность, так как третья сторона получает возможность простым способом “проиндексировать” все узлы, поддерживающие протокол, что даёт дополнительную информацию о сеансах, в которых данные узлы участвовали. Простые алгоритмы, основанные на знании секретного ключа, очень эффективны, но они работают только в ограниченном случае – например, для закрытого списка корреспондентов. Пример такого алгоритма, реализованного на уровне веба: проверяющий полномочия узла корреспондент отправляет запрос HTTP GET, где в качестве имени документа указан уникальный код, снабжённый меткой подлинности, вычисленной на основе секретного ключа; узел, обнаружив такой запрос и убедившись в его валидности, отвечает соответствующим значением, также с меткой подлинности; если же запрос невалидный, то узел отвечает стандартной ошибкой HTTP – тем самым он оказывается неотличим от обычного веб-сервера. Однако, действительно хорошо скрытая от активного “индексатора” аутентификация, – или, если говорить строго, семантически стойкая аутентификация, – возможна только на базе нескольких итераций “запрос-ответ” и, похоже, оказывается весьма сложной. Другими словами, с аутентификацией как раз и могут возникнуть основные проблемы на практике.

4.

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

5.

Выбранная модель угроз гарантирует, что третья сторона может использовать подставные узлы, имитирующие узлы-участники скрытого обмена сообщениями. Если протокол поддерживает возможность создания новых каналов обмена сообщениями и подключения новых узлов (а это часто необходимо), то возникает ещё одна непростая задача: как отличить деятельность атакующей третьей стороны от настоящих участников обмена? Одним из вариантов решения является удостоверение ключей новых участников ключами уже работающих узлов (напоминает практику PGP). Однако, в случае скрытого протокола, процедура такого удостоверения может оказаться весьма сложной: как и где обмениваться подписями, не раскрывая факта такого обмена и идентификаторов узлов? Предположим, что скрытым протоколом хотят воспользоваться два участника, которые раньше сообщениями не обменивались – понятно, что у них, в общем случае, нет возможности отличить подставной узел от подлинного корреспондента. Можно было бы заблаговременно раздать всем некоторый общий открытый ключ, который будет удостоверять новые узлы, но в таком случае возникает центральная схема, которой все должны доверять, в том числе, новые участники. К тому же, схема неустойчива к утрате соответствующего секретного ключа (и кто его будет хранить?) Похоже, кроме варианта с предварительным распределением многих личных ключей, остаётся только офлайновый обмен (опять же, вспоминается PGP).

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



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

Подробное описание того, как работает (на практике) технология ESNI (зашифрованное поле SNI TLS), в текущей draft-версии. В качестве примера я использую тестовый сервер tls13.1d.pw, где ESNI поддерживается (а поддерживающий браузер – это Firefox свежих версий, но ESNI там нужно включить, вместе с DNS-over-HTTPS).

Введение

ESNI требует публикации данных в DNS, а также наличия дополнительного секретного ключа на сервере (это, обычно, другой ключ, а не соответствующий TLS-сертификату). В DNS публикуется запись, содержащая публичный серверный ключ ESNI. Для получения общего секрета – используется протокол Диффи-Хеллмана (см. ниже). Кроме того, в DNS-записи публикуются: интервал валидности ключа (и других параметров) ESNI (начальная и конечная даты); тип используемого симметричного шифра (используется для зашифрования имени сервера); контрольная сумма (часть значения SHA-256); номер версии протокола и ожидаемая длина данных ESNI. DNS-запись ESNI – это только информация о ключе (или ключах – их может быть несколько) и о шифре (шифрах), это публичная информация, она не содержит скрываемого имени сервера.

TLS-сервер, поддерживающий ESNI, должен уметь распознавать расширение ESNI в составе сообщения ClientHello и обрабатывать его, проверяя корректность. Кроме того, сервер должен отправить ответную, серверную запись ESNI. В этой записи содержится копия значения, полученного в зашифрованном виде от клиента. ESNI-ответ сервера так же передаётся в зашифрованном виде.

Логика работы следующая: браузер получает из DNS открытый ключ DH (протокола Диффи-Хеллмана), генерирует сеансовый симметричный ключ, зашифровывает имя сервера и некоторое уникальное значение, которые, вместе со своим открытым ключом DH, передаёт серверу в расширении ESNI ClientHello. (Я описывал этот процесс в отдельной записке.)

Часть DNS

Сейчас в качестве DNS-записи используется запись типа TXT. Возможно, в дальнейшем для ESNI выделят новый тип записи.

Структура записей, размещаемых в DNS, следующая (в нотации языка Go):

type KeyShare struct {
Group uint16
Key []byte
}
type ESNIKeys struct {
Version uint16
Checksum [4]byte
Keys []KeyShare
Ciphers []uint16
PaddedLength uint16
NotBefore uint64
NotAfter uint64
Extensions []byte
}

Данные кодируются в Base64 и публикуются в TXT-записи под именем, содержащим префикс _esni. Для tls13.1d.pw это _esni.tls13.1d.pw. Сейчас для tls13.1d.pw опубликована ESNI-запись с двумя ключами (X25519 и P-384).

Подробные описания полей:

Version

длина: два байта; значение: 0xFF01 (действующая версия, draft).

Checksum

длина: четыре байта; это контрольная сумма – первые четыре байта значения SHA-256 от всех данны структуры ESNI (для вычисления контрольной суммы поле Checksum во входных данных заполняется нулями).

Keys

длина этого поля зависит от криптосистем и их количества; здесь содержится список (массив) структур, в типовом, для TLS, формате: первые два байта содержат общую длину блока данных (в байтах), далее, для каждого элемента списка, указывается тип криптосистемы (идентификатор группы, два байта), длина (два байта) конкретной записи ключа, само значение ключа.

Рассмотрим список ключей для ESNI-записи _esni.tls13.1d.pw (здесь используется два ключа – X25519, P-384):

длина списка: 0x0089;

тип первой криптосистемы (X25519): 0x001D;
длина записи ключа (32 байта): 0x0020;
значение ключа: […] /32 байта, в криптосистеме X25519 – ключи записываются 32-байтовыми последовательностями/;

тип второй криптосистемы (P-384): 0x0018;
длина записи ключа (97 байтов): 0x0061;
значение ключа (97 байтов): 0x04[…] /ключ является точкой на кривой, записываются координаты точки, первый байт обозначает тип представления (04 – несжатое), далее – две координаты по 48 байтов: 48 + 48 +1 = 97; 48 байтов – соответствуют разрядности криптосистемы).

Ciphers

это тоже список, длина зависит от количества поддерживаемых шифров. Для tls13.1d.pw используется один шифр:

длина списка: 0x0002 (два байта – идентификатор шифра);
идентификатор шифра (AES_128_GCM): 0x1301.

PaddedLength

два байта, поле содержит максимальную длину записи списка имён в ESNI, значение, которое поддерживает сервер tls13.1d.pw: 0x0080.

NotBefore, NotAfter

всем привычные таймстемпы по восемь байтов. Интервал валидности записи.

Extensions

расширения ESNI. Они пока не определены спецификацией. Поле является списком, соответственно, пустой список представляется двумя нулевыми байтами (список, имеющий длину нуль): 0x0000.

Часть TLS

Обработка ESNI TLS-сервером, который действует на tls13.1d.pw, проводится при получении ClientHello. Это сообщение содержит список расширений, для каждого расширения указывается тип (подробнее про то, как работает TLS можно почитать в техническом описании протокола). Тип расширения ESNI – 0xFFCE (это Draft-версия!). Внутренняя логика обработки: расширения передаются в виде списка TLS-структур, длина списка задаётся первыми байтами, далее идут типы расширений и соответствующие им поля данных, каждое поле начинается байтами, содержащими длину.

Например, вот тип, описывающий клиентское расширение ESNI (представление внутри сервера):

type TLS_ESNI struct{
Cipher uint16
Group uint16
KeyShare []byte
Digest []byte
Data []byte
}

Клиент передаёт в ESNI идентификатор шифра (Cipher), тип криптосистемы (Group) и свой ключ DH (KeyShare), а также контрольную сумму (Digest) и, собственно, полезные данные (Data) – nonce и зашифрованное имя сервера. Значение nonce (16 байтов) – это то самое значение, которое сервер должен передать в ответном ESNI-расширении.

При разборе полей KeyShare, Digest, Data – используется обычный для TLS подход: первые два байта интерпретируются как длина записи, далее, в зависимости от типа, аналогичным способом разбираются поля данных.

Конкретно в моём тестовом сервере обнаружение и обработка ESNI устроены так: на первом шаге читаются поступившие TLS-записи, делается попытка разобрать их по TLS-сообщениями и построить список таких сообщений, если попытка удалась, то в списке выполняется поиск ClientHello; если удалось найти ClientHello, то для него вызывается парсер, который разбирает сообщение, выделяя “голову” и список расширений (здесь речь про все расширения, не только про ESNI); на втором шаге – делается попытка в списке расширений ClientHello найти расширения, необходимые для сессии TLS 1.3 (SupportedVersions и др.), это позволяет распознать нужную версию протокола; если минимальный набор расширений найден, то делается попытка обнаружить расширение ESNI. И, соответственно, если расширение обнаружено, то оно передаётся парсеру ESNI, который разбирает его по полям. Далее, используя секретный ключ для ESNI, сервер, выполняя алгоритм DH, вычисляет симметричный ключ и пытается расшифровать данные (поле Data). Если расшифрование завершилось успешно, то заполняется структура с данными из ESNI и генерируется ответное серверное расширение. (Отмечу, что это логика работы, подходящая именно для тестового сервера.)

Если возникли какие-либо ошибки в обработке ESNI, то, в действующей версии сервера, TLS-соединение завершается (с ошибкой уровня TLS). Здесь для ESNI получилось исключение, потому что многие другие ошибки и несоответствия сервер специально игнорирует, чтобы показать на странице результатов сведения об этих ошибках; наиболее существенный пример – это значение Finished клиента: некорректное значение, вообще говоря, является фатальной ошибкой, но тестовый сервер его игнорирует, выводя, впрочем, пометку (воспроизвести эту ситуацию довольно сложно – потребуется специальная утилита на стороне клиента). В дальнейшем я планирую сделать такую же мягкую обработку ошибок и для ESNI. Как минимум, наличие дефектного расширения ESNI не должно считаться фатальным.

Успешно расшифрованное имя из ESNI используется сервером, собственно, оно просто позже выводится на страницу результатов. Расшифрованное значение nonce – сервер записывает в ответное ESNI-сообщение. На этом обработка ESNI заканчивается. Если же расширение ESNI не обнаружено, то сервер продолжает обрабатывать соединение по обычной схеме. Все сообщения можно посмотреть на веб-странице, которую выводит сервер.

Да, кстати, из недавно добавленных улучшений на tls13.1d.pw: сейчас там выводятся симметричный ключ и вектор инициализации ESNI.



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

Речь про Интернет. Скрытые сервисы, это замаскированные сервисы, которые “внешне” неотличимы от каких-то “обычных” сервисов, но выполняют другую функцию. Своего рода стеганография на уровне установления соединения. Хороший пример – прокси-сервер, имитирующий обычный веб-узел, доступный по HTTPS. Для обнаружения скрытого сервиса нужно знать секретный ключ. Например, для веб-узла, который является прокси, логика такая: клиент, установив соединение, передаёт специальный запрос, где указывает особое значение (для его генерации нужен секретный ключ); сервер проверяет полученное значение и, если оно соответствует коду доступа, подтверждает, что здесь есть скрытый сервис. Дальше клиент уже обращается к скрытому сервису по другому протоколу. Если же передано неверное значение, то сервер просто возвращает обычный код ошибки HTTP.

Внешний сканер, которому не известен подходящий секрет, не может отличить обычный веб-узел от веб-узла со скрытым сервисом, так как даже если сканер пытается передать некоторую имитацию запроса к скрытому сервису, в ответ он получит лишь сообщение об ошибке – одинаковое и для узла со скрытым сервисом, и для обычного веб-узла. Прикрытием, в случае HTTPS, может служить любой сайт, например, какой-нибудь тематический сайт, сайт-справочник и так далее. HTTPS удобен потому, что это один из самых распространённых протоколов, кроме того, он использует TLS, а это позволяет прозрачно провести аутентификацию узла. Есть черновик RFC, в котором описан именно этот подход.

Сходную схему можно построить и на уровне TLS (но, опять же, применительно к веб-узлу). Например, так (ниже – копия моего текста, который я некоторое время назад публиковал в Facebook).

Используем TLS, поверх которого работает HTTPS на некотором сервере: то есть, это 443/tcp и всё должно выглядеть как веб-сервер для внешнего сканера. Для упрощения, считаем, что у нас TLS 1.3, вообще, это не так важно, но 1.3 подходит лучше.

В TLS, при установлении соединения, передаются специальные сообщения. В них есть разные поля, мы будем использовать те, которые предназначены для отправки (псевдо)случайных данных – клиентская пара: ClientRandom и SessionID, серверная – ServerRandom и SessionID (значение не изменяется). Оба поля, в общем случае, содержат 32 байта данных (там есть всякие ограничения, но мы будем считать, что их нет). Цель – получить наложенный протокол, который активируется внутри TLS-трафика и только при наличии некоторых ключей, а в прочих случаях – неотличим от HTTPS/TLS. Механизм следующий.

Конфигурация: сервер, реализующий веб-узел – показывает произвольный сайт, например, трансляции новостей; клиент, планирующий использовать скрытый сервис (прокси), подключается к серверу по 443/tcp через TLS.

Клиент знает определённые ключи: общий с сервером симметричный ключ Ck (пусть, 256 бит; это ключ только для конкретного клиента – на сервере ведётся реестр ключей по клиентам, см. ниже), публичный асимметричный ключ сервера Pk (может отличаться от ключа, используемого сервером в TLS; считаем, что это та или иная криптосистема на эллиптической кривой, поэтому ключ тоже 256-битный, в совсем уж технические детали я постараюсь не вдаваться). На первом этапе клиент должен передать серверу свой идентификатор (некоторое число), который позволит серверу найти на своей стороне соответствующий секретный симметричный ключ (копию Ck).

Клиент, устанавливая TLS-соединение, использует серверный ключ Pk (он должен быть известен заранее – это важно) для получения общего секрета протокола Диффи-Хеллмана (DH), вычисленную открытую часть DH клиент отправляет в поле ClientRandom, а на основе полученного секрета – генерирует сеансовый симметричный ключ (Tk) и зашифровывает свой идентификатор, который, в зашифрованном виде, записывает в поле SessionID.

Получив TLS-сообщение, сервер вычисляет секрет DH (из ClientRandom), на его основе получает симметричный ключ и расшифровывает идентификатор из SessionID. На данном этапе сервер просто запоминает полученные значения: так как возможны атаки с повтором, сервер продолжает обработку TLS-соединения, запомнив полученные ключи и идентификатор. Очевидно, что это могут быть и не ключи вовсе. В ответном TLS-сообщении – сервер отправляет свою часть DH (от другого ключа) в ServerRandom (SessionID сервер использовать не может, потому что, согласно спецификации TLS, должен передать в ответ то же значение, которое получил от клиента).

К этому моменту, клиент уже может перейти на защищённый обмен сообщениями на уровне TLS (согласно спецификации). Клиент аутентифицирует сервер средствами TLS. Если аутентификация прошла успешно, то клиент переходит к следующему шагу получения доступа к скрытому сервису (если нет, то клиент просто закрывает TLS-сессию). На основе ответа сервера, клиент вычисляет вторую итерацию DH (это DH для скрытого сервиса) – получает второй общий секрет DH_2, используя ServerRandom, и второй открытый параметр DH_s, который нужно передать серверу (см. ниже). Итак, клиент, на настоящий момент, аутентифицировал сервер и получил следующие (дополнительные) криптографические параметры: общий секрет DH_2, сеансовый симметричный ключ Tk, общий с сервером симметричный ключ Ck. На основе этих значений, используя ту или иную хеш-функцию (например, SHA-256), клиент генерирует секретный тег (256-битное значение). Клиент соединяет это значение с параметром DH_s и записывает в начало полезной нагрузки первого TLS-сообщения, это, условно говоря, magic number. Полезная нагрузка передаётся в зашифрованном виде, поэтому третья сторона тег (magic number) – не видит.

Сервер, получив TLS-сообщение, расшифровывает его штатным образом, интерпретирует начальные данные как тег, выделяет DH_s, вычисляет общий секрет DH_2, генерирует ключи (секретный ключ Ck, соответствующий клиенту, сервер определил ранее – теперь этот ключ пригодился), вычисляет значение хеш-функции и сравнивает результат с тегом. Если значения совпали, то сервер считает, что осуществляется доступ к скрытому сервису и начинает проксировать полезные данные TLS в сторону этого сервиса (там уже есть своя аутентификация и пр.) Если тег не совпал, то сервер вспоминает, что он является веб-сервером с HTTPS и отвечает обычной (подходящей) HTTP-ошибкой, так как подставной тег выглядит как неверный HTTP-запрос.

Посмотрим, что видит третья сторона, пассивно просматривающая трафик: третья сторона видит, что установлено TLS-соединение на 443/tcp (HTTPS) и узлы обмениваются информацией по TLS, внутрь заглянуть нельзя, так что проксируемый трафик не виден. Так как, при правильно выбранной криптосистеме, значения ClientRandom, ServerRandom и SessionID вычислительно неотличимы от случайных (но при этом реально являются параметрами DH и зашифрованным идентификатором), то никаких подозрений, сами по себе, вызвать не могут.

Что видит активная третья сторона, проводящая сканирование узлов? Такой сканер не знает клиентских ключей, поэтому, даже попытка имитировать доступ к скрытому сервису, путём записи произвольных значений в поля TLS-заголовков и в данные сессии – будет приводить к ошибке HTTP (либо к ошибке TLS). Понятно, что обычное HTTPS-сканирование показывает, что там какой-то веб-сайт. Таким образом, узел, реализующий скрытый сервис, неотличим от обычного веб-узла с HTTPS. Более того, если сканеру известна часть ключей, например, открытый ключ сервера Pk, то сканер всё равно не сможет сгенерировать корректный тег, так как должен для этого знать ещё и валидный клиентский ключ. До момента появления корректного тега сервер ведёт себя в точности как HTTPS-узел, поэтому, опять же, не отличается от обычного сайта.

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

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



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

Реализовал шифр “Кузнечик” на ассемблере, входящем в комплект компилятора языка Go. Ассемблерный вариант довольно простой и работает в 128-битных регистрах архитектуры amd64, это даёт большой прирост производительности.

“Кузнечик” из ГОСТ Р 34.12-2015 – это современный российский блочный симметричный шифр. Несколько лет назад я реализовал его на языке Go. Вариант на языке высокого уровня – не слишком быстро работает, поэтому я переписал шифр на ассемблере, для архитектуры x64/amd64. Использовал ассемблер (точнее – псевдоассемблер), встроенный в Go.

Новый вариант называется GOSThopper и использует 128-битную арифметику, доступную на современных процессорах с архитектурой x64 (далее я буду называть её amd64, именно такое обозначение использует компилятор Go). Основная идея оптимизации такая: написать быструю реализацию обработки блока в “длинных” регистрах процессора – шифр как раз использует 128-битный блок, так что разрядность хорошо подходит. В системе команд процессоров amd64 (точнее, в некотором расширении системы команд, но это детали, так как сейчас данное расширение доступно практически везде) – есть нужный набор “атомарных” инструментов: быстрая загрузка данных из памяти, XOR, сдвиги и произвольный доступ к байтам.

Тут необходимо напомнить, как операции шифра “Кузнечик” оптимизируются чисто алгебраически. Структура шифра такова, что все его преобразования можно предвычислить для значений отдельных байтов и хранить в довольно больших (64K) таблицах (если говорить строго, то это матрицы коэффициентов, но здесь я буду называть их просто таблицами). Аналогичная оптимизация известна для всех AES-подобных шифров.

После того, как таблицы подготовлены и загружены в память – работа алгоритма, реализующего шифр, сводится к выбору элементов из таблицы и сложению этих элементов при помощи операции XOR. То, какой элемент таблицы использовать, определяется значениями текущего байта входного текста, ключа и номером раунда. Элементы в таблицах – 128-битные, совпадающие по длине с блоком. То есть, при должной сноровке, алгоритм становится очень быстрым. Особенно сильно можно ускориться, если использовать регистровую арифметику подходящей разрядности, что я и попытался сделать. Ссылки на полные исходные тексты даны в конце записки. Но, фактически, весь ассемблерный код сводится к фрагменту, представленному на скриншоте (синтаксис у ассемблера Go своеобразный).

Assembly code listing screen copy.

Краткие пояснения к алгоритму (с. 19-33): выполняем сложение блока (PXOR) с ключом текущего раунда, результат помещаем в регистр X0 (так обозначаются 128-битные регистры XMMn); извлекаем младший (под нулевым номером) байт длинного регистра (PEXTRB), умножаем его на 16 (SHLQ) и складываем (ADD) с базовым адресом таблицы (он ранее загружен в регистр DX); полученное смещение (оно находится в регистре AX) теперь указывает на нужный элемент таблицы предвычисленных значений, извлекаем этот элемент и суммируем с предыдущим (PXOR со значением в X2, первый элемент последовательности просто записывается в X2 – с. 25). Написано “в лоб”, нет дополнительных проверок валидности адресов и размеров массивов (предполагается, что эти параметры контролируются снаружи данной процедуры).

Ассемблерный код выполняет только преобразование блока, а вычисление таблиц, разворачивание ключей – всё это осталось в коде на Go.

Итак, новая реализация в несколько раз быстрее предыдущих. Простой тест на процессоре Intel i5-9600K показывает скорость около 180 мегабайт в секунду для зашифрования и около 140 Мбайт/сек для расшифрования (процедура расшифрования существенно отличается от зашифрования, использует дополнительную таблицу, а кроме того, я её совсем не оптимизировал, потому что для основных современных режимов использования блочных шифров процедура расшифрования не нужна). Так или иначе, 180 мегабайт – это неплохой результат. Предыдущая версия, только на Go, но с unsafe-конструкциями, показывает на том же процессоре лишь около 45 Мбайт/сек. На небольших массивах данных – скорость ассемблерной версии ещё заметно возрастает, поскольку процессор эффективно использует кэш-память.

Как я уже не раз упомянул, это ассемблер архитектуры amd64, поэтому на других платформах, например, на различных ARM, данный ассемблерный код использовать не получится. Так что пришлось дополнить модуль “заглушками”, а точнее – реализациями шифра на “чистом Go”. Компилятор Go позволяет прозрачно генерировать межплатформенный код, для этого используются специальные директивы, в данном случае: // +build !amd64. Другими словами, в файле с исходным кодом, предназначенным для всех платформ, кроме amd64, указывается директива “// +build !amd64”, а для amd64 – код выносится в файлы с постфиксом _amd64. (Конкретно: docipher_amd64.go – содержит объявления функций; docipher_amd64.s – код на ассемблере.) Соответственно, данный модуль успешно компилируется на разных платформах, я проверил на ARM. Однако скорость работы на платформах, отличных от amd64, будет ниже на порядки (в сто раз и даже более) – это связано с тем, что используется простая реализация шифра (файл docipher.go). Но amd64 – более чем распространённая архитектура, поэтому новый быстрый шифр может оказаться полезен. (Не исключено, кстати, что возможны весьма экзотические конфигурации, когда платформа amd64 не содержит каких-то “длинных” команд, но это нужно проверять отдельно.)

Нередко спрашивают: как простым способом использовать реализацию шифра для обработки некоторого потока данных? Понятно, что непосредственно шифр применять нельзя, нужна некая обёртка, называемая “режимом использования шифра“. Для упрощения реализации примеров “из жизни” я добавил в модуль пару простых функций, реализующих зашифрование и расшифрование в режиме счётчика (CM_Encrypt(), CM_Decrypt()). Это готовый инструмент для работы с потоком данных: то есть, в качестве аргументов функции передаются вектор инициализации, ключ и слайс (массив) данных; функция возвращает обработанный слайс той же длины. Важное замечание: конкретный вектор инициализации нельзя повторно использовать для зашифрования с одним и тем же ключом; учитывайте и тот факт, что начальное значение увеличивается внутри процедуры на единицу с каждым обработанным блоком (см. исходный код).

В реализации режима счётчика нет аутентификации (это важно!). Для аутентифицированного варианта можно использовать штатный режим GCM из Go-пакета crypto/cipher. В модуле есть нужный интерфейс, поэтому шифр элементарно подключается к GCM. Примеры есть в исходном коде, а краткое описание дано ниже.

Тут необходима ещё одна оговорка: “Кузнечик” не стандартизован для применения в режиме GCM. В российских криптографических ГОСТах пока что вообще нет аналогичного режима (AEAD), но, вероятно, он вскоре появится, и это, конечно, будет не GCM, а вариант разработанного российскими специалистами режима, который сейчас называется MGM (Multilinear Galois Mode).

Логика использования в режиме счётчика:

CM_CipherText := gosthopper.CM_Encrypt(0x1234567, Key, SourceText) 
[...]
CM_PlainText := gosthopper.CM_Decrypt(0x1234567, Key, CM_CipherText)

(Здесь 0x1234567 – это вектор инициализации, начальное значение счётчика, собственно говоря. Данное значение использовано для примера, оно не является секретным, но, повторюсь, нельзя повторно использовать одно значение с тем же ключом. Важно учитывать, что значение счётчика увеличивается с каждым блоком на единицу внутри процедуры, поэтому для нового вызова с тем же ключом – начальное значение тоже должно увеличиться, как минимум, на число_блоков + 1, иначе возникнет повтор. То есть, данные функции являются только демонстраторами общего принципа, а управление инциализацией режима счётчика представляет отдельную задачу.)

Логика в режиме GCM (import “crypto/cipher”; AD – дополнительные данные, которые передаются в открытом виде):

kCipher, err := gosthopper.NewCipher(Key) 
[...]
kuznecGCM, err := cipher.NewGCM(kCipher)
[...]
GCM_sealed := kuznecGCM.Seal(nil, GCM_nonce, PT, AD)
[...]
GCM_opened, err := kuznecGCM.Open(nil, GCM_nonce, GCM_sealed, AD)

Режим GCM в пакете crypto/cipher реализован полностью, но тоже требует инициализации (GCM_nonce). В целом, GCM является более совершенным режимом, чем простой режим счётчика (собственно, GCM – это улучшенная разновидность режима счётчика).

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

Исходный код:
основной файл – gosthopper.go;
реализация шифра на ассемблере – docipher_amd64.s;
объявления функций – docipher_amd64.go;
реализация только на Go для платформ, отличных от amd64 – docipher.go.

Всё вместе в одном архиве: gosthopper1.tar.gz.

Подробные примеры использования и тесты: test_gosthopper.go

Пакет называется gosthopper. Для того, чтобы его использовать, нужно тем или иным способом разместить (например, просто скопировать) файлы с исходными кодами в директорию пакетов вашей инсталляции Go. (См. переменную окружения GOPATH.) Файл test_gosthopper.go относится к пакету main, поэтому его лучше собирать в другой директории. Внутри файлов – много дополнительных пояснений (англ.).

Вопросы, пожелания – приветствуются в комментариях или по электронной почте.



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

На днях NIST опубликовал результаты первого этапа программы по стандартизации постквантовых криптосистем (распределения ключей и электронной подписи), во второй раунд прошло 26 предложений из 82 поступивших на рассмотрение в самом начале (к первому этапу из них было допущено 69). Это повод очередной раз вспомнить о том, что постквантовые криптосистемы сейчас составляют одно из основных направлений современной криптографии. Постквантовые – это такие криптосистемы, которые устойчивы к взлому с использованием универсального квантового компьютера. Давно известно, что распространённые сейчас асимметричные криптосистемы (RSA, ECDSA, используемые разновидности протокола Диффи-Хеллмана) полностью уязвимы к атакам, использующим квантовые алгоритмы “нахождения периода”, это, в принципе, алгоритм Шора.

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

Скорее всего, на начальном этапе та или иная постквантовая криптосистема будет использоваться параллельно с классической. То есть, результаты обмена ключами по постквантовому алгоритму и по классическому будут смешиваться при генерации симметричных ключей, это обеспечит стойкость в том случае, если постквантовая криптосистема окажется уязвимой для классических атак (такие атаки вполне могут появиться раньше самого квантового компьютера). В браузере Chrome уже проводился эксперимент по использованию постквантовой криптосистемы New Hope.

Думаю, можно предположить, что приняты будут постквантовые криптосистемы, основанные на свойствах эллиптических кривых. Собственно, несколько лет назад я специально разместил на dxdt.ru краткую заметку, на которую можно сослаться, когда процесс выбора криптосистем, как говорится, сойдётся. Заметка даже специфицирует конкретное направление (изогении). Эллиптические кривые хорошо подходят потому, что, во-первых, они являют собой фундаментальную теоретико-числовую структуру, имеющую огромное чисто математическое значение; во-вторых, эллиптические кривые хорошо изучены внутри теоретической криптографии; в-третьих, для них наработано большое число библиотек и прикладных алгоритмов, которые, к тому же, тщательно проверены и оптимизированы. (Эти преимущества перечислены и в публикации NIST.)

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



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

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

Эти квантовые методы должны использовать какие-то свойства взламываемого шифра, которые хорошо проецируются именно на возможности квантовых вычислений. Пример. Предположим, что для для заданного шифра можно построить некоторую периодическую функцию, разбивающую всё пространство ключей на интервалы. Период этой функции может эффективно определить квантовый компьютер (это типичная задача, так, на поиске периода основан квантовый алгоритм Шора факторизации чисел). Знание периода позволяет путём анализа нескольких пар, состоящих из открытого текста и соответствующего ему шифротекста, определить, в какой именно из интервалов попадает секретный ключ, а дальше уже перебирать значения только из этого интервала, который, предположим, невелик. (Можно переформулировать: пусть обнаруживается период функции, определяющей возможные ключи – в таком случае, зная период, можно будет проверить только их, прыгая по значениям.) Пары открытых текстов и шифротекстов часто известны на практике. Скажем, HTTPS, использующий TLS в качестве транспорта, подразумевает передачу куки-файлов или известных заголовков запросов. При этом режим счётчика (GCM и пр.) основан на зашифровании последовательных значений. Это грубое изложение, но оно математически сходно с атаками, которые уже несколько лет обсуждаются в публикациях, применительно к различным режимам симметричных шифров.

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



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

В криптографической библиотеке для Arduino используется российский шифр “Магма” (ГОСТ 34.12-2015). Я сравнил свою реализацию “Магмы” из библиотеки с другим “малым” шифром – Speck, который был предложен в 2013 году специалистами АНБ. Чтобы сравнение было точнее, я также реализовал Speck, использовав inline-ассемблер (ссылка на исходный код – в конце записки).

Прежде всего – краткое описание шифра Speck. Это новый блочный шифр, специально разработанный для программной реализации в микроконтроллерах, то есть, вычислителях с сильно ограниченными ресурсами. Как и многие другие современные шифры (а также и “Магма”), Speck построен на базе простой “раундовой” функции, которая последовательно вызывается с разными ключами, проводя преобразование блока данных. В Speck эта же функция используется для разворачивания основного ключа в последовательность ключей раундов. Это первое существенное отличие от “Магмы”, в котором разворачивание ключа сводится к копированию (см. описание “Магмы”). Спецификация Speck вводит несколько вариантов шифра, имеющих разную разрядность блока и ключа. Я использовал наиболее близкий к “Магме” вариант: 64-битный блок и 128-битный ключ (256-битного, по ключу, варианта Speck для 64-битного блока – нет, поэтому сравнить шифры на одной длине ключа нельзя). От варианта к варианту в Speck различаются количество раундов и часть параметров раундовой функции (разрядность логических сдвигов).

На картинке ниже – подробная схема раунда Speck (шифрование).

Входной блок разбивается на две части. Это, опять же, одно из распространённых и хорошо проверенных решений в схемах шифров. Левая часть (A1 на схеме) циклически сдвигается вправо на 8 разрядов, суммируется с правой частью (A0) по модулю 232 (то есть, как два 32-разрядных числа “естественным образом”, без переноса и знака; операция обозначена символом ⊞). На следующем шаге выполняется сложение по модулю 2 (XOR, ⊕) с раундовым ключом. После чего результат обработки полублока суммируется (опять же, XOR) со вторым полублоком, который перед этим циклически сдвигается на три разряда влево.

В шифре “Магма” используется схема Фейстеля, в которой полублоки, после применения раундовых преобразований, переставляются. Раунд шифра Speck также можно привести к схеме Фейстеля, разделив на два этапа. Первый этап включает преобразования левой части, за которыми следует перестановка полублоков. Схема:

Второй этап – состоит в применении сдвига и XOR к результату первого этапа. Схема:

То есть, с некоторой долей условности можно сказать, что Speck построен из двух, применяемых последовательно, конструкций Фейстеля.

Раунды выполняются циклически. Для используемого варианта шифра число повторений равняется 27. Таким образом, требуется 27 раундовых ключей, каждый разрядностью 32 бита. Схема их получения из исходного, 128-битного, ключа состоит в последовательном применении той же раундовой функции к ключу, предварительно разбитому на слова, разрядность которых совпадает с разрядностью полублока. То есть, 128-битный ключ (16 байтов) даёт нам четыре слова по 32 бита. Эти слова служат левым и правым полублоком в раундовой функции. Ключом (Ki на схеме) при этом является номер раунда. Другими словами: первый раундовый ключ – это младшие 32 бита исходного, основного ключа (используются прямо). Второй раундовый ключ – младшие 32-бита результата применения раундовой функции к младшим 64 битам основного ключа (очевидно, что сюда, в качестве правого полублока, входит первый раундовый ключ); и так далее, подробнее можно посмотреть в исходном коде.

Переходим к сравнению шифров. Ассемблерная реализация позволяет посчитать примерное число тактов, необходимых для выполнения преобразований шифра. Для Speck подсчёт дал 1951 такт – столько занимает полная реализация, с обращением к памяти, где хранятся раундовые ключи и блок открытого текста, с выгрузкой результата. Сюда не входит код получения последовательности раундовых ключей (развёртывания ключа). 1951 такт, в пересчёте на байт данных (блок состоит из 8 байтов), даёт: 1951/8 = 244 такта на байт (приблизительно). В исходной работе, со спецификацией Speck, авторы приводят результат от 118 до 160 тактов для аналогичного 8-разрядного микроконтроллера, но здесь не учитываются операции по загрузке/выгрузке блоков, так что результаты довольно близки (кроме того, мой вариант можно оптимизировать).

Реализация “Магмы” из библиотеки для Arduino показывает следующие результаты: 9092 такта полный код, соответственно, 1136 (приблизительно) тактов на байт (отмечу, что показатели близки к реализациям AES). Существенный вклад в это число вносит реализация подстановок, где много обращений к памяти с достаточно сложными преобразованиями указателей. Этот код можно оптимизировать, вплоть до того, что сами подстановки разместить в регистрах (для 16 полубайтовых подстановок, закрывающих одну позицию во входном полублоке, достаточно восьми байтовых регистров; однако вся таблица подстановок в регистрах типичного микроконтроллера вряд ли поместится – для неё нужно 64 регистра). Правда, вычисление номера регистра и обращение к нему потребует дополнительных усилий, объём кода заметно возрастёт, а для “Магмы” он и так не маленький. Тем не менее, вряд ли выигрыш по тактам составит более двух раз. Из-за подстановок – “Магма” медленнее, чем Speck, тут ничего нельзя поделать.

Другим параметром, по которому можно сравнить шифры, является объём требуемой памяти данных. Для Speck нужно 108 байтов для раундовых ключей. Реализация “Магмы” требует 128 байтов (здесь больше раундов – 32). При этом, если требуется экономия памяти, “Магма” позволяет прямо использовать в качестве раундовых ключей слова из состава основного ключа. Со Speck такой фокус не пройдёт, потому что здесь сложная функция разворачивания ключа. Однако оптимизация всё равно возможна: раундовые ключи можно вычислять в процессе выполнения раундов шифра. Впрочем, такой вариант приведёт к тому, что минимум в два раза возрастёт число тактов, поэтому Speck приблизится к “Магме”. Для хранения таблицы подстановок “Магма” требует ещё 8*8=64 байта.

Реализация “Магмы” существенно превышает показатели Speck по объёму кода. И тот, и другой шифр вполне укладываются в разумные рамки, но код шифра Speck – очень компактный. Побайтового сравнения объёмов я не привожу.

Итак, “Магма” уступает Speck по показателям, относящимся к программной реализации. Неудивительно: “Магма” очень старый шифр, разработанный ещё в 70-х годах прошлого века. Как ни странно, в этом состоит его преимущество, если говорить об оценках стойкости. Этот шифр до сих пор входит в рекомендации ГОСТ и для него пока не опубликовано криптоанализа, существенно снижающего стойкость, хотя сломать шифр пытались многократно. Speck – новый шифр, выполненный в современных традициях, задуманный специально для микроконтроллеров, этим обусловлены его преимущества. Для данного шифра также пока не опубликовано криптоанализа, существенно снижающего стойкость (известные работы – предлагают академические атаки, не пригодные для практических сценариев), но нужно учитывать малое распространение и новизну шифра. Отмечу также, что Speck далеко не единственный новый шифр, ориентированный на “Интернет вещей”, однако является одним из самых простых, быстрых и компактных. Тем не менее, преимущество в скорости и возможность экономии нескольких десятков байтов – не всегда являются решающими факторами, в том числе, для устройств с малой вычислительной мощностью. Например, в случае передачи раз в час команд от устройства к устройству, разница в несколько миллисекунд, требующихся для получения криптограммы, не играет роли. (С другой стороны, каждый такт – это расход энергии элемента питания, что может оказаться существенным для устройств с экстремально низким энергопотреблением.)

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

Приложение: исходный код реализации Speck для Arduino (только зашифрование), также содержит дополнительные функции для шифрования тестового вектора и проверки правильности работы.



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

В продолжение предыдущей заметки – посмотрим, как устроены симметричные блочные шифры. Примером послужит шифр “Магма”, в версии ГОСТ Р 34.12-2015, с картинками. Симметричными называют шифры, для которых ключ расшифрования можно легко получить из ключа зашифрования. В современных симметричных шифрах – эти ключи просто совпадают. Блочный шифр, в отличие от потокового, работает с блоками данных фиксированной длины (разрядности), измеряемой в битах. Например, шифр AES работает с блоками разрядности 128 бит. А “Магма” – 64 бита.

Получив на вход блок открытого текста и ключ, преобразование, называемое шифром, выводит блок шифротекста, той же разрядности. Соответствие между блоками открытого текста и блоками шифротекста задаёт значение ключа. “Магма” использует ключ длиной 256 бит. Современные шифры строятся из некоторых элементарных операций над блоками. Наборы таких операций обычно объединяют в раунды. Раунды повторяются несколько раз.

В “Магме” 64-битный блок разделяется на две равные части, над которыми производятся операции раунда. Эти операции включают в себя (в порядке выполнения): сложение с ключом раунда; подстановки; циклический сдвиг; сложение с половиной блока. Заканчивается раунд перестановкой полублоков местами. Раунд показан на схеме ниже:

Magma Round

A1 и A0 – две части входного блока: соответственно, они содержат по 32 разряда каждая. На вход цепочки раундовых операций поступает значение полублока A0. Для каждого раунда используется свой ключ. На схеме ключ обозначен Ki. Раундовый ключ также имеет длину 32 бита (разряда), то есть, совпадает с разрядностью половины блока. Сложение блока со значением ключа (операция обозначена символом ⊞) выполняется по модулю 232 – это эквивалентно “естественному”, для вычислительной техники, сложению двух 32-битных чисел (без знака). Над результатом сложения выполняются подстановки по таблице подстановок. Таблицы часто называют S-boxes. “Магма” использует 4-битные подстановки, отдельные для каждого полубайта из 32-битного блока. Логика тут следующая: 32-битный блок разбивается на 8 4-битных частей, каждое из получившихся значений (0..15, так как битов – четыре) заменяется на соответствующее ему значение из таблицы подстановок; таблиц восемь – по одной для каждой позиции 4-битного значения внутри 32-битного блока.

В версиях шифра, предшествовавших ГОСТ Р 34.12-2015, таблицы подстановок предлагалось выбирать отдельно для каждой сети обмена сообщениями и держать в секрете. То есть, таблицы позволяли повысить стойкость шифра. При этом, впрочем, неверно выбранные подстановки стойкость могут заметно снизить, а раскрыть секретные подстановки реально, если атакующий может зашифровывать произвольные тексты с известным ключом. В ГОСТ Р 34.12-2015 – значения подстановок зафиксированы.

Значение 32-битного блока после подстановок циклически сдвигается влево на 11 разрядов (то есть, биты сдвигаются влево, а выбывшие разряды вдвигаются справа в том же порядке). После операции сдвига, значение поразрядно суммируется с блоком A1 по модулю 2, это логическая операция XOR (обозначена символом ⊕ на схеме).

В заключении раунда – A1 и A0 меняются местами. То есть, A0 переходит в следующий раунд без изменений, но становится на место A1. Всего раундов 32. Последний раунд отличается тем, что 32-битные блоки не меняются местами, а просто объединяются: A0 присоединяется к A1 справа. Это замыкает всю конструкцию, позволяя использовать её без изменений для расшифрования: операция расшифрования отличается только обратным порядком раундовых ключей.

Раундовых ключей – 32. Каждый имеет разрядность 32 бита. Эти ключи получаются из основного ключа шифрования при помощи алгоритма развёртывания ключа. В “Магме” этот алгоритм очень простой. Исходный ключ содержит 32 байта (256/8=32). 32 байта – это 8 раундовых ключей, каждый по четыре байта; на 32 раунда – ключи копируются, с той лишь разницей, что последние восемь используются в обратном порядке. Схема:

Round Keys

Работа шифра выражается в “перемешивании” битов открытого текста, схему перемешивания определяет ключ (и таблицы подстановок, но мы считаем, что они неизменны). Преобразования можно визуализировать. На картинке ниже – разным значениям битов соответствуют синий и жёлтый цвета, раунды выполняются сверху вниз (самая верхняя строка – открытый текст).

Magma Operation

Посмотрим, насколько важны параметры и базовые преобразования, выбранные для построения шифра. Важным инструментом криптоанализа является изучение работы шифра при малых отличиях открытых текстов. На картинке ниже – распространение изменений между раундами “Магмы”: два открытых текста отличаются значением одного бита, ключ используется одинаковый; синим цветом отображены разряды, значения которых совпали между собой; зелёным – отличающиеся разряды.

Avalanche-1

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

No S-boxes

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

Попробуем изменить другой параметр – число разрядов, на которые циклически сдвигается блок в каждом раунде. Используем сдвиг на 12 разрядов, вместо 11 (подстановки и другие преобразования – без изменений).

Wrong Rotation

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



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

Old Cracked EngineБольше и больше пишут про “квантовые каналы” связи, которые “абсолютно” защищены “квантовой криптографией”, и только они смогут спасти от “квантового компьютера”. Здесь интересны несколько моментов.

1.

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

2.

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

3.

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

4.

Квантовые компьютеры. Симметричные системы (шифры), вроде AES, сохраняют стойкость: сейчас считается, что появление квантовых компьютеров достаточной мощности приведёт лишь максимум к “квадратичной” оптимизации перебора. Это очень оптимистичная, в отношении квантового компьютера, оценка, потому что речь идёт о квантовых операциях, реализующих алгоритм поиска – а это совсем другое дело, по сравнению с классическим CPU. То есть, для сохранения стойкости, разрядность ключа симметричного шифра нужно будет увеличить в два раза. Соответственно, AES с 256-битным ключом обеспечит достаточную степень защиты (эквивалентную 128 битам). Если стороны успели в защищённом режиме обменяться гигабайтом ключевого потока, то его, даже при расточительном использовании в качестве ключей AES, хватит надолго. (256 бит – это 32 байта, которые можно смело использовать для шифрования AES примерно 2^32 блоков, каждый блок – это 16 байтов, то есть, одного ключа хватит для 16 * 2^32 ≈ 64 гигабайт передаваемых данных; и это только один ключ, 64 байта из гигабайта).

5.

Квантовые компьютеры достаточной разрядности – полностью побеждают распространённые сейчас асимметричные криптосистемы. Если только такие компьютеры возможны. Как ни странно, первыми падут суперсовременные криптосистемы на эллиптических кривых: это связано с тем, что они имеют малую разрядность, а способность квантового компьютера взламывать такие системы находится в прямой зависимости от числа доступных кубитов. То есть, компьютера, взламывающего ECDSA с ключами в 256 бит, ещё недостаточно для того, чтобы атаковать RSA с разрядностью в 2048 бит. Так что тут у RSA есть преимущество. Другое дело, что как только сумеют построить квантовый компьютер с разрядностью в 256 кубитов, масштабирование на 2048 – вряд ли потребует долгих лет.

6.

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

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

7.

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



Комментарии (5) »
Навигация по запискам: Раньше »