Криптографические протоколы
8.1. Понятие криптографического протокола
Протокол – это последовательность шагов, которые предпринимают две или более сторон для совместного решения некоторой задачи. Все шаги выполняются в строгой очередности, и ни один из них не может быть сделан прежде, чем закончится предыдущий.
Криптографические протоколы предназначены для обмена информацией в ситуации, когда цели участников могут быть нарушены злоумышленником (когда под угрозой оказывается конфиденциальность, целостность или подтверждаемость сообщений). Шаги протокола выстраиваются так, чтобы цели участников оказались выполнены, а цели злоумышленника – нет. Последовательность шагов по формированию и проверке электронной подписи с участием удостоверяющего центра – пример такого протокола.
Ценность протокола как конструкции в том, что он достигает целей, недостижимых действиями одного участника и потому недоступных никакому алгоритму.
Простейший пример – разделение секрета. Требуется разделить сообщение между несколькими участниками так, чтобы ни один из них не мог извлечь из своей части никаких сведений, а собравшись вместе, они могли восстановить исходное. Для двух участников достаточно сгенерировать случайное число R той же длины, что и сообщение M, вычислить S = R⊕ M и раздать участникам R и S по отдельности. Каждая часть сама по себе случайна и о сообщении не говорит ничего (аналогия с одноразовым блокнотом), а вместе они дают R⊕ S = M.
Другие распространенные задачи, которые требуют применения криптографических протоколов:
- Тайное голосование. Каждый участник подает голос анонимно; проголосовать дважды нельзя; голосовать могут только зарегистрированные участники; при этом каждый может убедиться, что его голос учтен правильно. Анонимность и проверяемость – два почти взаимоисключающих требования, и хороший протокол должен, в первую очередь, снять это противоречие.
- Слепая подпись. Подписывающий заверяет документ, не имея возможности ознакомиться с его содержанием (нотариус, заверяющий закрытое завещание).
- Групповая подпись. Проверяющий убеждается, что документ подписан членом группы, но не может определить, каким именно; при споре подпись может быть раскрыта.
- Одновременная подпись. Нескольким сторонам необходимо подписать документ так, что каждая сторона получает гарантию, что ее подпись появится только вместе с подписью других сторон.
Все эти задачи формулируются в требованиях к тому, что узнают и чего не узнают стороны по завершении обмена; требования к преобразованию данных вторичны. Именно этим различаются протоколы и алгоритмы. Алгоритм преобразует данные, и его свойства проверяются на нем самом: у шифра есть стойкость, у хэш-функции – сопротивление коллизиям, и эти свойства не зависят от того, кто и когда алгоритм применяет. Свойства протокола зависят от порядка сообщений, от того, что каждая сторона проверяет перед следующим шагом, и от того, как участники поступают при отклонении от сценария. Протоколы, по-разному составленные из одних и тех же безупречных примитивов, могут обеспечивать совершенно разные уровни защиты.
Это обстоятельство определяет и способ атаки. Противнику незачем вскрывать шифр, если можно повторить чужое сообщение, вклиниться между собеседниками или вынудить стороны выбрать слабейший из доступных им алгоритмов. Основные уязвимости криптографических протоколов кроются в непредусмотренных сценариях взаимодействия сторон (хотя протокол, основанный на слабых примитивах, наследует их слабости).
8.2. Протоколы аутентификации
Самая многочисленная группа криптографических протоколов решает задачу аутентификации: одна сторона должна убедить другую, что она именно та, за кого себя выдает.
Пусть в дальнейшем сторона A, проверяемый, должна доказать свою подлинность стороне B, проверяющему. Аутентификация на основе знания предполагает, что проверяемому известен некоторый секрет (например, пароль) и он должен доказать проверяющему владение им.
Простейший протокол – аутентификация на основе открытого пароля. A передает свой идентификатор и пароль, B просто сверяет полученную пару с хранящейся у него базой. Протокол не зависит ни от каких примитивов и совершенно небезопасен по двум причинам. Во-первых, пароль передается по сети открыто: маршрут прохождения пакетов через интернет может быть каким угодно, содержимое трафика доступно для анализа на промежуточных узлах, а в публичной беспроводной сети и вовсе любому участнику. Во-вторых, пароли хранятся у проверяющего в открытом виде, и однократного доступа к его базе достаточно, чтобы впоследствии выдавать себя за любого пользователя.
Первое возражение снимается, если передавать по сети не сам пароль, а его хэш. Так устроена аутентификация на основе хэшированного пароля: система хранит пары «идентификатор – хэш пароля» и сравнивает с сохраненным значением хэш, вычисленный на стороне пользователя. Необратимость хэш-функции гарантирует, что противник, перехвативший хэш, не восстановит по нему пароль.
Это не обеспечивает, однако, подлинности. Противнику незачем восстанавливать пароль: достаточно перехватить хэш и предъявить его системе самому (потребуется заменить клиентскую программу, чтобы она не хэшировала полученное значение повторно, но это задача тривиальная). Такая атака называется атакой повторного воспроизведения (replay). Заменив пароль его хэшем, мы не устранили передаваемый по сети секрет, а лишь сменили его вид: теперь доступ дает хэш.
Обобщим наблюдение. Подлинное сообщение, записанное противником и переданное повторно, остается подлинным: хэш сходится, подпись верна, шифрование корректно. Ни один примитив от повторного воспроизведения не защищает, потому что сообщение не подделано и защищать здесь нечего. Различие между исходной передачей и повторной лежит целиком во времени, то есть вне самого сообщения. Отсюда требование свежести: обмен должен быть устроен так, чтобы ответ, пригодный сейчас, не годился в следующий раз. Средствами служат одноразовые случайные значения, счетчики и отметки времени.
Хороший криптографический протокол строится исходя из предположения, что противник может перехватывать любые передаваемые сообщения, блокировать их и посылать собственные. В хорошем протоколе аутентификации по сети не передается ничего, что открывало бы перед противником новые возможности. Протоколы, в которых проверяемая сторона подтверждает знание ключа, не раскрывая самого ключа, называются протоколами строгой аутентификации.
Строгая односторонняя аутентификация на основе случайных чисел
Стороны заранее располагают общим ключом K и договорились о симметричном алгоритме шифрования.
- A → B: A
- B → A: r
- A → B: [r]K
A сообщает о желании получить доступ. B генерирует случайное число r (вызов) и отправляет его A. A шифрует полученное число ключом K и возвращает результат. B расшифровывает сообщение и убеждается, что получилось отправленное им число.
Поскольку ключ K известен только A и B, проверяющий убеждается, что имеет дело именно с A. Перехват здесь бесполезен: ключ по сети не передается, а каждый сеанс использует новое случайное число, и записанный ответ на прошлый вызов к новому вызову не подойдет.
Шаг 3 может выполнять программа на рабочей станции или отдельное устройство; и то и другое называют OTP-токеном (one-time password, одноразовый пароль). Наиболее защищенный вариант – смарт-карта или USB-устройство, хранящее ключ внутри и требующее ввода PIN-кода для выполнения операции.
Строгая двусторонняя аутентификация
В предыдущем протоколе B убеждается в подлинности A, но не наоборот. Схема достраивается до взаимной:
- A → B: A
- B → A: r1
- A → B: [B, r1, r2]K
- B → A: [r1, r2]K
Запись [B, r1, r2]K означает сообщение, состоящее из имени B, полученного от него случайного числа и другого случайного числа, сгенерированного стороной A, целиком зашифрованное ключом K. Имя B вложено внутрь шифрования, чтобы противник не мог тут же вернуть это сообщение самой A: ключ K один на оба направления, и без имени ответ A годился бы как ответ B. Получив сообщение, B убеждается в подлинности A (только A могла зашифровать ключом K число r1, отправленное мгновением раньше). На шаге 4 A убеждается в подлинности B, поскольку никто другой не смог бы дешифровать предыдущее сообщение, извлечь из него r2 и составить новое.
Шаг 4 выглядел бы проще, если бы B возвращал r2 в открытом виде: этим он тоже показал бы, что сумел дешифровать сообщение, а значит, владеет ключом. Но такая версия дает противнику, наблюдающему за сетью, пару «открытый текст – шифртекст»: шифртекст передавался на шаге 3, а соответствующий ему открытый текст складывается из сообщений шагов 2 и 4. Современный шифр должен обладать стойкостью к атаке с известным открытым текстом, но протокол, раздающий даром материалы для анализа, ослабляет примитив, на котором построен.
Аутентификация на основе асимметричного алгоритма
Общий секретный ключ нужен не всегда. Если B располагает открытым ключом стороны A (обозначим его PK(A)), достаточно следующего обмена, где H – хэш-функция:
- A → B: A
- B → A: H(r), B, [r, B]PK(A)
- A → B: r
B шифрует случайное число открытым ключом A, и извлечь это число может только владелец соответствующего закрытого ключа. Предлагается самостоятельно порассуждать, зачем на втором шаге передается хэш и какие угрозы возникают при его отсутствии.
Чтобы применять эту схему на практике, необходимо гарантировать, что PK(A) действительно принадлежит стороне A, что возвращает к сертификатам и удостоверяющему центру. В свою очередь, для протоколов на основе симметричного алгоритма остается незакрытым вопрос: откуда у сторон берется общий ключ K. Хотя мы уже знаем, что для его передачи может использоваться шифрование с открытым ключом, хотелось бы решить проблему проще. Ответ дают протоколы обмена ключами.
8.3. Протоколы обмена ключами
Протокол обмена ключами – это протокол, с помощью которого знание секретного ключа (предназначенного, как правило, для последующего симметричного шифрования) разделяется между двумя или более сторонами, причем противник, перехватывающий все пересылаемые сообщения, получить этот ключ не может.
Различают три вида таких протоколов.
Протоколы передачи ключей. Ключ генерируется одной из сторон и доставляется остальным в защищенном виде. Так устроена гибридная схема из предыдущей лекции, где сеансовый ключ шифруется открытым ключом получателя.
Протоколы совместной выработки общего ключа. Ключ вычисляется обеими сторонами из их собственных вкладов и по каналу связи не передается вообще.
Схемы предварительного распределения. Каждый участник заранее получает секретную часть исходной ключевой информации, общая часть размещается на общедоступном сервере, и ключ для связи любой пары абонентов вычисляется из имеющихся у них частей. Применяются, чтобы сократить объем хранимой и распределяемой секретной информации. От такой схемы требуют устойчивости (раскрытие части ключей при компрометации или сговоре некоторых абонентов не должно раскрывать остальные) и гибкости (возможности быстро исключить скомпрометированные ключи и подключить новых абонентов).
Алгоритм Диффи-Хеллмана
Наиболее известный протокол совместной выработки ключа. Он надежен при условии, что противник может перехватывать данные, но не модифицировать их. Стойкость опирается на трудность дискретного логарифмирования: не известно эффективного способа решить уравнение aˣ mod n = b относительно x.
- Участники выбирают два больших числа g и p, p простое. Эти числа не секретны и могут быть определены заранее или даже зашиты в программное обеспечение.
- Каждый участник генерирует случайное число (a и b соответственно) и хранит его в тайне.
- Первый вычисляет A = gᵃ mod p и пересылает второму, второй вычисляет B = gᵇ mod p и передает первому.
- Первый возводит полученное значение в степень a по модулю p, второй в степень b. Оба получают одно и то же число K = gab mod p, которое и берется в качестве секретного ключа.
Сокращенно:
- A → B: g, p, A = gᵃ mod p
- B → A: B = gᵇ mod p
- A, B: K = gᵃᵇ mod p
Располагая значениями g, p, A и B (все они передаются по незащищенному каналу), но не зная a и b (эти значения не передаются никогда), противник не может вычислить K.

Рис. 8.1. Алгоритм Диффи-Хеллмана
Эта схема имеет три преимущества:
- Секрет не передается. По каналу проходят только A и B, само значение K не появляется в нем ни в каком виде. Противнику нечего перехватывать: у него нет зашифрованного ключа, к которому можно было бы подбирать средства дешифрования сейчас или через двадцать лет.
- Ключ не выбирает никто в отдельности. Итоговое значение зависит от вкладов обеих сторон, и ни одна из них не может навязать другой заранее известное ей значение. Это существенно в ситуациях, когда участники обмена доверяют друг другу не полностью.
- Секрет может быть одноразовым. Если числа a и b выбираются заново для каждого сеанса связи и уничтожаются по его окончании, то полная запись перехваченного трафика не поможет противнику даже в том случае, если впоследствии он завладеет долговременными ключами участников: сеансового секрета к этому моменту физически не существует.
Атака «человек посередине» (Man in the Middle)
Противник не обязан действовать в лоб. Прямой атаке протокол Диффи-Хеллмана не поддается, однако уязвим, если противник способен не только перехватывать сообщения, но и блокировать их и отправлять собственные.
Противник (E) вклинивается в сеанс связи, выдавая себя стороне B за сторону A, а стороне A за сторону B. Он перехватывает сообщение:
- A → B: g, p, A = gᵃ mod p
генерирует число c и подменяет перехваченное сообщение на:
- A {E} → B: g, p, C = gᶜ mod p
Ответное сообщение
- B → A: B = gᵇ mod p
подменяется сообщением:
- B {E} → A: C = gᶜ mod p
В результате сторона A вычислит секретный ключ K1 = gᵃᶜ mod p, а сторона B – K2 = gᵇᶜ mod p. Оба ключа известны противнику, и он читает всю переписку. Чтобы остаться необнаруженным как можно дольше, он передает каждой стороне сообщения другой, предварительно дешифровав их одним ключом и снова зашифровав другим.

Рис. 8.2. Атака «человек посередине» на протокол обмена ключами по алгоритму Диффи-Хеллмана
Математика протокола не нарушена: дискретный логарифм противник не вычислял и ни одного секретного числа не узнал. Протокол дал ровно то, что обещал, то есть общий секрет с тем, с кем шел обмен сообщениями. Он не обещал, что собеседником была сторона B. Аутентификации в нем нет, и добавить ее средствами самого протокола невозможно: у A и B нет ничего, что отличало бы подлинного собеседника от подставного.
Аутентифицированный обмен ключами
Выход состоит в том, чтобы связать одноразовое значение с чем-то, чем располагает только подлинная сторона. Каждый участник подписывает свое значение gᵃ mod p долговременным закрытым ключом, а получатель проверяет подпись открытым ключом из сертификата. Подмена обнаруживается на проверке: противник не изготовит подписи под собственным значением.
Роли ключей при этом разделяются. Долговременный ключ отвечает за подлинность, одноразовый за секретность, и ни один не делает работу другого. Отсюда вытекает свойство, называемое совершенной прямой секретностью (perfect forward secrecy): компрометация долговременного ключа не раскрывает сеансы, состоявшиеся до нее, поскольку сеансовые секреты вырабатывались из одноразовых значений и давно уничтожены. Противник, годами накапливавший перехваченный трафик и добравшийся наконец до ключа сервера, не получает ничего (в отличие от передачи сеансового ключа, зашифрованного открытым ключом получателя – утечка закрытого ключа раскроет весь накопленный архив).
Аутентифицированный обмен ключами требует, чтобы стороны заранее располагали подлинными открытыми ключами друг друга, то есть нужна инфраструктура открытых ключей. Для сети, в которой каждый участник должен иметь возможность связаться с каждым, существует и другой путь: доверенный посредник, хранящий ключи всех участников и ручающийся за них. Так устроен протокол Kerberos.
8.4. Протокол Kerberos
Kerberos – один из общедоступных протоколов аутентификации. Он разработан в середине 1980-х годов, когда инфраструктуры открытых ключей еще не существовало, и построен целиком на симметричном шифровании.
Задача протокола – обеспечить косвенное доверие двух участников, которые все вместе доверяют посреднику и имеют с ним общие ключи. В среде со множеством пользователей и множеством сервисов такой посредник снимает необходимость каждому пользователю отдельно регистрироваться на каждом сервисе и управлять огромным количеством ключей. Kerberos применяется в доменах Active Directory и в распространенных Unix-системах и остается одним из самых распространенных протоколов аутентификации.
Участниками протокола Kerberos являются:
Клиент– пользователь или система, желающие получить доступ к ресурсам или функциям некоторого сервера (например, сервера электронной почты). В результате выполнения шагов протокола клиент должен подтвердить свою подлинность.
Сервер– система, которая должна убедиться в подлинности клиента, прежде чем предоставлять ему свои услуги.
KDC (KeyDistributionCenter) – центр распределения ключей – сторонний посредник между клиентом и сервером, который «ручается» за подлинность клиента. В центре распределения ключей хранятся так называемые долговременные ключи каждого пользователя системы, а также серверов. Долговременный ключ пользователя, как правило, вычисляется из его пароля.
Ключ для связи с сервером клиент получает от центра распределения ключей вместе со структурой, которую в терминологии Kerberos называют мандатом или билетом (в оригинале – ticket).
Рассмотрим упрощенную схему аутентификации:
1. Клиент → KDC: ID клиента, ID сервера
2. KDC → Клиент: [сеансовый ключ]K(клиент), [ID клиента, адрес клиента, время, сеансовый ключ]K(сервер)
Клиент просит доступ к определенному серверу. Проверив, что клиент с таким ID есть в базе, KDC вырабатывает одноразовый сеансовый ключ для этой пары и отдает его дважды: под долговременным ключом клиента и под долговременным ключом сервера. Вторая часть и называется билетом. Идея в том, что билет не отправляется второму участнику будущего взаимодействия (серверу), а вручается непосредственно клиенту, который при этом не может его прочитать или изменить, потому что ключа, которым он зашифрован, у него нет.
Полученный билет клиент предъявляет серверу.
3. Клиент → Сервер: билет, [ID клиента, время]сеансовый ключ
Сервер расшифровывает билет своим долговременным ключом, извлекает сеансовый ключ и им расшифровывает вторую часть. Совпадение ID клиента в обеих частях доказывает, что билет предъявил тот, кому он выдан: скопировавший чужой билет сеансового ключа не знает и такой приписки не составит. Приписка называется аутентификатором.
4. Сервер → Клиент: [время из аутентификатора]сеансовый ключ
Ответом сервер подтверждает свою подлинность: составить его мог только тот, кто сумел дешифровать билет, то есть владелец долговременного ключа сервера.
В рассмотренной схеме долговременный ключ клиента (по существу – его пароль) нужен при каждом обращении к каждому новому серверу. Значит, система должна либо всякий раз спрашивать пароль, либо держать в памяти выведенный из него ключ. Поэтому в реальном протоколе клиент на шаге 1 называет не прикладной сервер, а службу выдачи билетов (TGS, Ticket-Granting Service) – второй вход в тот же центр распределения ключей. Полученный на шаге 2 билет называют билетом на получение билетов (TGT, Ticket-Granting Ticket). Дальше схема повторяется, но роль долговременного ключа клиента играет сеансовый ключ из TGT.
5. Клиент → TGS: ID сервера, TGT, [ID клиента, время]сеансовый ключ
6. TGS → Клиент: [сеансовый ключ для связи с сервером]сеансовый ключ, билет к запрошенному серверу
Долговременный ключ клиента участвует один раз за сеанс работы. Дальше клиент предъявляет TGT сколько угодно раз и с каждым полученным билетом сам повторяет шаги 3 и 4, не обращаясь к посреднику.
С проектной точки зрения важны следующие моменты:
- Долговременный ключ предъявляется однократно. Тот же прием мы видели в аутентифицированном обмене ключами: долговременный секрет предъявляется как можно реже, а повседневную работу ведут одноразовые ключи.
- Посредник знает все. Центр распределения ключей хранит долговременные ключи всех участников, а значит, способен выдать билет на любое имя и прочитать любой сеанс. Его компрометация означает компрометацию всей системы целиком, в точности как компрометация корневого удостоверяющего центра. Более того, центр распределения ключей обязан постоянно отвечать на запросы и является единой точкой отказа по доступности: пока он недоступен, не работает никто.
- Свежесть достигается отметками времени. Требование свежести (ответ, пригодный сейчас, не должен годиться в следующий раз) в протоколах строгой аутентификации выполнялось вызовом: проверяющий присылал случайное число и ждал ответа именно на него. Здесь ту же задачу решают часы. Клиент вкладывает в каждое обращение текущее время, а служба выдачи билетов и сервер отказываются продолжать протокол, если метка слишком стара (например, отстает от их собственного времени более чем на пять минут). Сообщение, перехваченное злоумышленником и повторенное позже, в это окно не попадет. Выигрыш в том, что вызов требует лишнего обмена сообщениями и связывает ответ с одним-единственным собеседником, тогда как билет с меткой времени клиент получает однажды и предъявляет многократно, не обращаясь всякий раз к посреднику.
Расплата за удобство состоит в том, что часы всех участников должны идти согласованно, а обеспечить это протокол не может. Ни один его шаг не проверяет, верно ли идут часы у собеседника; расхождение проявится как отказ в доступе, причем без указания настоящей причины. Точность хода обеспечивается извне: отдельной службой времени, порядком ее применения и контролем за его соблюдением. Протокол, безупречный математически, работает лишь постольку, поскольку соблюдено условие, лежащее целиком вне его.
Kerberos – пример надежного криптографического протокола. Однако используя такие «надежные» схемы, никогда не следует забывать, что безопасность должна быть комплексной. Протокол выполняется участниками не вручную, а с помощью соответствующего программного обеспечения. Так, в 2000 году в программной реализации протокола, разработанной в самом MIT, были обнаружены ошибки переполнения буфера, позволяющие удаленному атакующему получить полный контроль над системой (привилегии root), где эта программа выполнялась.
8.5. Генерация случайных чисел
В каждом разобранном протоколе встречались случайные числа. При рассмотрении криптографических примитивов мы также постоянно использовали случайные числа (из них порождаются ключи симметричных алгоритмов, пары ключей асимметричных, инициализационные векторы режимов шифрования, соли при хранении паролей и т.д.), но в протоколе их роль особая. Число, отправленное проверяющим в качестве вызова, не является секретами, от него требуется только не повториться и не дать себя предсказать. Это свойство позволит отличить нынешний сеанс от прошлого, обеспечить свежесть, сделать бесполезной запись чужого обмена. Ни одно из этих построений не работает, если противник может предсказать выбранное значение.
Проблема в том, что программа случайных чисел выдавать не умеет. Алгоритм детерминирован: при одних и тех же входных данных он всегда дает один и тот же результат. Поэтому применяются генераторы псевдослучайных чисел (ГПСЧ) – алгоритмы, выдающие последовательность, элементы которой почти независимы друг от друга и подчиняются заданному распределению (обычно равномерному, при котором все значения на заданном интервале равновероятны).
Генератор инициализируется начальным значением и далее выдает последовательность, полностью определяемую этим значением: запуск с тем же началом дает тот же результат. Очередное значение вычисляется из одного или нескольких предыдущих, поэтому последовательность периодична. Как только встретится цепочка элементов, уже встречавшаяся ранее, дальше все повторится в прежнем порядке.
К генератору, применяемому в криптографии, предъявляют три требования.
- Непредсказуемость. Зная алгоритм генератора и все выданные им ранее элементы, но не зная начального значения, противник не может вычислить следующий элемент. Это главное требование, и обычные генераторы, входящие в стандартные библиотеки языков программирования, ему не удовлетворяют.
- Достаточно большой период. Последовательность рано или поздно начинает повторяться, и период должен быть таким, чтобы противник не смог зафиксировать зацикливание.
- Равномерность. Все элементы последовательности одинаково вероятны. Если генератор выдает биты, количество нулей и единиц должно быть примерно одинаковым.
Генератор растягивает начальный секрет в длинную последовательность, но проблема начального значения остается открытой. Его берут из энтропии – физических процессов, исход которых не зависит от вычислений. Операционная система собирает энтропию из интервалов между нажатиями клавиш, перемещений указателя, задержек дисковых операций и сетевых событий. В системах с повышенными требованиями применяются аппаратные источники, дающие настоящий белый шум: измерение тепловых флуктуаций, запись радиоэфира на свободной частоте, дробовой шум полупроводникового перехода.
Таким образом, справедливо правило: фактическая длина ключа определяется энтропией породившего его источника. Ключ в 256 бит, выбранный из тысячи возможных вариантов, перебирается за тысячу попыток, и никакой алгоритм этого не исправит.
Несколько примеров показывают, что происходит при нарушении этого правила.
Первый пример самый частый и внешне безобиднее прочих. Генераторы, входящие в стандартные библиотеки языков программирования, обычно устроены как линейный конгруэнтный: очередное значение вычисляется из предыдущего по формуле
xn+1 = (axn + c) mod m
Такой генератор быстр и дает приемлемое распределение, поэтому для моделирования или перемешивания списка вполне годится. Но по нескольким выданным значениям восстанавливаются и параметры, и текущее состояние, после чего вычисляется вся последовательность, прошлая и будущая.
При этом разработчик не может увидеть проблему, ведь он просто вызывает функцию получения случайного числа для генерации ключа. Это относится даже к надежным библиотекам. В 2006 году в одной из распространенных сборок криптографической библиотеки из генератора по недосмотру была удалена строка, дававшая основную часть энтропии. В течение двух лет ключи, порождавшиеся на этой сборке, выбирались фактически из нескольких десятков тысяч вариантов, и злоумышленнику достаточно было перечислить их заранее.
В схемах подписи на эллиптических кривых при каждом подписании используется случайное число, применяемое однократно. Если оно совпадет для двух разных сообщений, закрытый ключ вычисляется из двух подписей решением системы уравнений. Именно так в 2010 году был раскрыт ключ, которым подписывалось программное обеспечение игровой приставки: разработчик подставил вместо случайного числа константу. Стоит лишний раз отметить, что требование случайности относится не только к ключам.
Во всех случаях ни один алгоритм не взламывается, ни один протокол не содержит ошибки в порядке шагов, но защита полностью отсутствует.
Наиболее разумный подход к построению генератора состоит в использовании надежного симметричного шифра. В режимах CTR и OFB шифр создает шифрующий поток, который для не знающего ключа противника неотличим от случайного шума. Этот поток можно использовать и как псевдослучайную последовательность, взяв истинно случайный одноразовый ключ. Операционная система ведет пул энтропии, пополняя его из всех доступных источников (в частности, современные процессоры содержат аппаратный источник шума) и переинициализируя генератор по мере накопления новых данных. Прикладной программе остается обратиться к системному интерфейсу (getrandom и /dev/urandom в Linux, BCryptGenRandom в Windows, SecRandomCopyBytes в macOS).
Стандарт NIST SP 800-90A описывает семейство генераторов DRBG, построенных на уже разобранных примитивах: на хэш-функции, на коде аутентификации HMAC и на блочном шифре в режиме счетчика. В ядре Linux применяется генератор на основе шифра ChaCha20.
8.6. Установление защищенного соединения
Все разобранные до сих пор конструкции применяются не поодиночке. Соединение с любым сайтом, вызов прикладного интерфейса, обращение мобильного приложения к своему серверу защищаются протоколом TLS (Transport Layer Security), и он показывает, как из перечисленных частей собирается работающее целое.
Задача ставится так: превратить открытый канал в защищенный, не имея между сторонами ничего общего заранее. Клиент знает доменное имя сервера и располагает хранилищем корневых сертификатов; больше у него нет ничего. Процедура установления защищенного канала называется рукопожатием. Оно осуществляется в несколько этапов.
- Согласование. Клиент сообщает, какие версии протокола и наборы алгоритмов он поддерживает, и сразу же присылает свое одноразовое значение для выработки ключа. Сервер выбирает из предложенного и отвечает своим одноразовым значением. К этому моменту обе стороны вычислили общий секрет по схеме Диффи-Хеллмана, и дальнейший обмен идет уже под шифрованием. Список поддерживаемых наборов передается открыто, и противник, вмешавшийся в первое сообщение, может вычеркнуть из него все сильные варианты, оставив слабейший. Такая атака понижения (downgrade) не требует вскрывать ни один алгоритм: стороны сами договорятся о том, который противник умеет читать.
- Подтверждение подлинности. Сервер предъявляет цепочку сертификатов и подписывает своим закрытым ключом хэш всего состоявшегося к этому моменту обмена. Клиент проверяет цепочку до корневого сертификата из своего хранилища, сверяет доменное имя с указанным в сертификате и проверяет подпись. Пропуск сверки имени (ошибка, неоднократно встречавшаяся в реальных библиотеках) обесценивает весь шаг: подойдет любой действительный сертификат, выданный на любое имя. На данном шаге снимается атака «человек посередине»: подменивший одноразовое значение не изготовит подписи под измененным обменом.
- Выработка ключей. Из общего секрета функцией выработки ключей получают не один ключ, а несколько: отдельные для каждого направления передачи и отдельные для разных стадий соединения.
- Завершение. Каждая сторона отправляет код аутентификации, вычисленный от всей состоявшейся переписки целиком, включая исходные списки наборов. Если противник изменил хоть одно сообщение по дороге, коды не сойдутся, и вычеркнутые варианты обнаружатся.
- Передача данных. Дальше все идет аутентифицированным шифрованием, причем в вычислении каждой записи участвует ее порядковый номер. Запись, изъятая, повторенная или переставленная противником, не пройдет проверку.
Протокол не вводит собственных примитивов, а строится на основе существующих, его суть – порядок действий и перечень обязательных проверок. Каждая из них закрывает конкретную возможность противника, и снятие любой обрушивает конструкцию целиком. Именно в этом слое обнаружено большинство известных уязвимостей защищенных каналов. Рассмотренное рукопожатие – результат тридцатилетнего исправления таких ошибок, и почти каждая его проверка появилась в ответ на конкретную атаку.
Рукопожатие TLS защищает одно соединение между двумя приложениями, и каждому следующему соединению требуется собственное. Приложение, которое TLS не использует, остается незащищенным. Наблюдатель внутри канала, даже не видя содержимого, видит адрес каждого сервера, к которому обращается машина, и время каждого обращения. Если защитить требуется весь обмен машины с сетью целиком, задача решается на другом уровне.
Туннелирование состоит в том, что пакет одного протокола целиком помещается в поле данных пакета другого протокола и передается по сети как обычная полезная нагрузка. На другом конце он извлекается и продолжает путь по назначению. Промежуточные узлы о вложенном пакете ничего не знают и обрабатывают только внешний. К защите информации прием изначально отношения не имел: он появился как способ провести пакеты одного протокола через сеть, которая этот протокол не поддерживает.
Существенным для защиты оказывается именно неосведомленность внешнего протокола о содержимом вложенного. Если внешний протокол шифрует и аутентифицирует свою полезную нагрузку, вложенный обмен получает защиту независимо от того, предусмотрена ли она в нем самом. Так устроена виртуальная частная сеть (VPN): между машиной и шлюзом устанавливается защищенное соединение, весь исходящий трафик машины направляется внутрь него, а шлюз распаковывает пакеты и отправляет их дальше от собственного имени.
Из такого устройства следуют три свойства, которых защита отдельного соединения не имеет.
- Действие на все приложения сразу. Защиту получают в том числе протоколы, шифрования не предусматривающие вовсе, причем от разработчика приложения не требуется ничего.
- Сокрытие служебной информации. Адреса назначения, номера портов и состав протоколов уходят внутрь туннеля. Наблюдателю виден только обмен со шлюзом. Объем передаваемых данных и моменты передачи при этом остаются на виду, и по ним о происходящем можно узнать больше, чем кажется.
- Перенос машины в другую сеть. Машина оказывается по ту сторону шлюза и получает доступ к ресурсам, открытым только изнутри. Это и есть исходное назначение технологии: подключение удаленного сотрудника или удаленного офиса к сети организации.
Границы конструкции задаются тем же, чем и ее устройство. Защита существует ровно на протяжении туннеля. За шлюзом пакеты идут обычным порядком, и если они не защищены сами по себе, остаток пути открыт; поэтому TLS внутри туннеля сохраняет смысл, закрывая участок, до которого туннель не достает. К тому, что происходит на концах, туннель отношения не имеет вовсе: зараженная машина отправит данные противнику через защищенный туннель ровно так же успешно, как без него.
Второе ограничение существеннее. Владелец шлюза видит весь распакованный трафик и знает, куда обращается каждая машина. Он занимает в точности то положение, которое занимал бы оператор связи, предоставляющий доступ. Здесь снова работает утверждение, зафиксированное при разборе удостоверяющих центров: криптография не создает доверие, а только переносит его. Коммерческая услуга VPN переносит доверие с оператора связи на владельца сервиса, и вопрос состоит в том, какая из двух сторон предпочтительнее при данной модели угроз. Ответ зависит от того, от кого защищаются, и в общем виде его не существует.
Назначение у распространенных реализаций общее, различаются они проектными решениями.
- IPsec работает на сетевом уровне и представляет собой набор протоколов с согласованием параметров при установлении соединения. Он наиболее общий и наиболее сложный, что стоило ему долгой истории уязвимостей в реализациях.
- OpenVPN строит туннель поверх разобранного выше рукопожатия TLS, переиспользуя готовую конструкцию вместе с ее проверенностью.
- WireGuard построен на противоположном решении. Набор алгоритмов зафиксирован в самом протоколе, согласования нет вовсе, и реализация значительно короче, а вероятность ошибки растет с объемом кода. Цена решения заключается в том, что замена алгоритма означает новую версию протокола и одновременное обновление обеих сторон, поскольку договориться о переходе стороны не могут.
8.7. Срок годности ключа и алгоритма
В TLS есть шаг, назначение которого стоит объяснить отдельно. Стороны начинают с согласования версии протокола и набора алгоритмов, хотя проще было бы раз и навсегда закрепить в стандарте одну версию и один набор (как в WireGuard). Согласование добавляет обмен сообщениями, усложняет реализацию и, как мы видели, само становится мишенью для атаки. Оно предусмотрено потому, что ни ключ, ни алгоритм не служат вечно.
Срок жизни ключа ограничивают по двум основаниям сразу: по времени и по объему обработанных данных.
Ограничение по объему следует из устройства самих алгоритмов. В потоковых режимах (OFB и CTR) повторение пары «ключ и инициализационный вектор» раскрывает оба сообщения, а вероятность такого повторения растет в соответствии с парадоксом дней рождения. Кроме того, чем больше материала зашифровано одним ключом, тем больше данных для криптоанализа.
Ограничение по времени следует из свойств не алгоритма, а обстановки. Вероятность того, что ключ утек и утечка осталась незамеченной, со временем только растет: меняются сотрудники, копируются резервные архивы, списываются носители. Ограниченный срок действия сам по себе ограничивает ущерб, потому что скомпрометированный ключ перестанет работать даже в том случае, если о компрометации никто так и не узнал. Отсюда правило: смена ключей должна быть штатной регулярной процедурой, а не следствием инцидента. Система, в которой замена ключа технически возможна, но на практике не производится, спроектирована плохо.
Сюда же относится и хранение. Ключ, лежащий в файле или находящийся в оперативной памяти, доступен всякому, кто получил доступ к системе. Поэтому в ответственных случаях применяют неизвлекаемый ключ: он порождается внутри устройства (смарт-карты, токена, аппаратного модуля безопасности) и наружу не выдается никогда, устройство отдает только результаты операций. Ключ при этом остается уязвимым (тот, кто получил доступ к устройству, может им пользоваться), но скопировать его нельзя, а значит, злоупотребление ограничено временем, пока устройство доступно, и обнаруживается по его пропаже.
Стойкость примитива убывает со временем сама собой: растет вычислительная мощность, накапливаются результаты криптоанализа. Мы рассматривали примеры отказа от DES и SHA-1, в обоих случаях замена начиналась задолго до появления практической атаки.
Отдельного упоминания заслуживает случай, когда убывание может оказаться не постепенным, а мгновенным. Речь о квантовых вычислениях. Существует расхожее представление, будто квантовый компьютер – просто очень быстрая машина, перебирающая все ключи разом. Выигрыш возникает лишь для задач определенной структуры и для разных задач оказывается принципиально разным.
Алгоритм Шора решает за полиномиальное время задачу разложения числа на множители и задачу дискретного логарифмирования, в том числе в группе точек эллиптической кривой. Иначе говоря, RSA, схема Диффи-Хеллмана, ECDSA и ГОСТ Р 34.10 перестают быть стойкими полностью. Увеличение длины ключа здесь не спасает: задача не становится труднее, она перестает быть трудной.
Алгоритм Гровера дает более скромный выигрыш: он ускоряет перебор множества из N вариантов до порядка √N операций. Для симметричного шифра это означает, что n-битный ключ обеспечивает стойкость, эквивалентную n/2 битам. Ответ известен: удвоить длину. AES-256 остается приемлемым, AES-128 теряет запас прочности.
Асимметрия последствий и составляет здесь главное. Симметричная криптография переживет появление квантового компьютера ценой настройки параметров. Асимметричная требует замены математического основания, то есть новых алгоритмов, новых стандартов, новых форматов сертификатов и обновления всего, что их использует.
Когда появится машина нужного масштаба, не знает никто: существующие установки далеки от него, поскольку устойчивые вычисления требуют миллионов физических кубитов на коррекцию ошибок. Решение о переходе, однако, не может ждать этого события, и причина формулируется как самостоятельный сценарий угрозы: «собирай сейчас, расшифруешь потом». Противник, располагающий ресурсами для длительного хранения, записывает зашифрованный трафик сегодня и расшифровывает его тогда, когда это станет возможно. Угроза, следовательно, актуальна уже сейчас, но не для всех данных, а лишь для тех, чья конфиденциальность должна сохраняться десятилетиями.
Ответом стала постквантовая криптография – асимметричные схемы, стойкость которых опирается на задачи, для которых квантового алгоритма не известно. По итогам открытого конкурса, устроенного по образцу конкурса AES, в 2024 году приняты первые стандарты: механизм выработки общего ключа ML-KEM и схемы подписи ML-DSA и SLH-DSA. Устройство их выходит за рамки курса, важны свойства, влияющие на проектирование: ключи и подписи заметно объемнее привычных, а срок изучения несопоставимо короче, чем у RSA. Последнее обстоятельство и объясняет принятую практику: новые схемы применяют вместе с классическими, вырабатывая общий секрет двумя способами сразу, так что результат остается стойким, пока не сломан хотя бы один из механизмов. Именно так с 2024 года работает выработка ключей в основных браузерах.
Система, проектируемая сегодня, почти наверняка переживет по меньшей мере одну смену примитивов. Способность заменить примитив, не переделывая систему, называется криптографической гибкостью и относится к архитектурным требованиям. Практически она означает следующее: алгоритм согласуется, а не зашит в код; идентификатор алгоритма хранится вместе с данными, чтобы старое можно было прочесть после перехода; в форматах предусмотрен запас на увеличение размеров ключей и подписей; переход на новый алгоритм не требует одномоментной остановки всей системы.
