«У меня обычно сюжетные песни,
а эта песня без сюжета.
Казалось бы, набор слов.
Нет — это песня-беспокойство» В. Высоцкий о песне «Парус»
Отдавая себе полный отчёт в том, что слово «криптоанализ», столь бесцеремонно использованное в заголовке статьи, может повлечь за собой настоящую бурю праведной критики, я всё-таки не спешу с ним расставаться и даже не собираюсь лишать приставки «крипто». Разумеется, сама по себе WinAPI-функция UuidCreate, ставшая предметом данного исследования, никакого отношения к криптографии не имеет. Её скромный удел — создавать уникальные идентификаторы (Universally Unique Identifiers), соответствующие требованиям RFC4122. Однако желающих колоть орехи микроскопом это почему-то не останавливает. Напротив, я всё чаще сталкиваюсь с попытками трудоустроить UuidCreate в качестве генератора псевдослучайных чисел (ГПСЧ).
Причин у такого явления, на мой взгляд, несколько. Во-первых, значения идентификаторов кажутся случайными и независимыми (трижды крестимся). Во-вторых, алгоритм UUID-генератора до сих пор не опубликован и, насколько мне известно, не существует ни одной заслуживающей доверия работы, где были бы описаны его свойства. В-третьих, официальный и полномочный ГПСЧ Windows — функция CryptGenRandom — проигрывает UuidCreate в простоте и доступности (достаточно вспомнить, что использование CryptGenRandom предполагает свидание с CryptoAPI, и далеко не у каждого программиста это свидание окончится бурным романом).
Так, например, встретившись однажды с коллегой из телекоммуникационной компании за кружкой добротного чешского пива, я узнал довольно любопытный факт. Оказывается, PIN-коды для карт экспресс-оплаты услуг этой «дальновидной» коммерческой организации генерируются на MS SQL Server с помощью функции newid (под чьей маской скрывается всё та же UuidCreate), причём никаких дополнительных преобразований над полученными значениями не производится. Решив поначалу, что «афтар жжот», я выразил эмоции в лучших традициях лошади Пржевальского, но уже через секунду об этом пожалел. Недоумение на лице собеседника совершенно точно указывало на искренность его слов и, будь у меня под рукой карманная Библия, уверен — он бы на ней поклялся. Библии не было, поэтому пришлось клясться на свежем номере журнала «MAXIM».
Тогда, к сожалению, мне не удалось найти весомых аргументов в пользу недопустимости использования UuidCreate в подобных целях, и до последней капли пива каждый из нас оставался при своём мнении. Однако откровения коллеги долгое время не давали мне покоя. Чтобы окончательно расставить точки над i, я решил проверить, насколько осуществимы следующие два сценария.
Оператор международной и сотовой связи ОАО «Дайте сала кило», известный после ребрендинга под торговой маркой «Алло, прачечная?», готовится представить своим абонентам новый тарифный план «Глухонемой». Помимо беспрецедентно низкой стоимости одной минуты разговора с коренным населением Земли Франца-Иосифа, тариф даёт возможность бесплатно скачать рингтон «Не слышны в саду даже шорохи».
Компания с оптимизмом смотрит в будущее и рассчитывает увеличить своё присутствие на рынке услуг междугородной связи. Предвидя большой рост числа абонентов, руководство поручает секретному отделу «Белые воротнички», куда входят сотрудники только с безупречной репутацией, выпустить новую серию карт экспресс-оплаты.
На это благое дело отводится специальный физический сервер, подготовкой которого занимается системный администратор Василий Раздолбаев (характер нордический, не женат).
Далее опишем события в хронологическом порядке.
Утро, 14 апреля, понедельник. Василий устанавливает на машину ОС Windows и MS SQL Server 2008.
Вечер 14 апреля, понедельник. Комиссия из числа сотрудников секретного отдела проверяет сервер на наличие шпионского ПО и выносит заключение: зловредного кода на машине не обнаружено. Сервер, не выключая, перемещают в подземный бункер, полностью отрезанный от внешнего мира (свинцовые стены толщиной 2 метра, перехват информации полностью исключён, доступ в помещение имеют только «Белые воротнички»).
Вечер, 15 апреля, вторник. Василий отправляется на самый беспощадный в его жизни корпоратив, устроенный по случаю выхода нового тарифа. Исполняет в караоке похабные песни, танцует голым на столе и пристаёт к Маше Ведро из бухгалтерии. Другими словами, ведёт себя как настоящий джентльмен.
А в это время... Пока системный администратор расслабляется, «Белые воротнички» генерируют PIN-коды новой серии карт экспресс-оплаты. В качестве источника случайных чисел используется T-SQL-функция NEWID (что, как будет показано ниже, равносильно вызову UuidCreate). Полученные таким образом PIN-коды сразу же наносятся на карты оплаты. Когда процедура полностью завершена, сервер под неусыпным контролем вооружённой до зубов охраны отвозят на ближайший металлургический завод, где и сжигают в доменной печи.
Утро, 16 апреля, среда. Новые карты экспресс-оплаты поступают в продажу. Василий Раздолбаев находит себя в мусорном баке за пределами МКАД.
Внимание, вопрос: может ли Василий, не прибегая к ректальному криптоанализу сотрудников секретного отдела, восстановить PIN-коды всей серии карточек, и что ему для этого нужно предпринять?
Предположим, сервер не уничтожили. Более того, системный администратор сумел получить к нему доступ только в среду, когда карточки уже появились в продаже. Остаётся ли у Василия шанс заполучить PIN-коды, учитывая, что они были удалены из базы данных «Белыми воротничками»?
Чтобы более-менее внятно ответить на эти интригующие вопросы, нам предстоит заглянуть под капот UuidCreate.
К сожалению, имеющаяся в нашем распоряжении официальная документация (статья в MSDN [4] и текст RFC4122 [1]) может пролить свет только на структуру возвращаемого значения, так что пользы от неё не больше, чем от ранее упомянутого журнала «MAXIM».
Определение функции (rpcdce.h):
RPCRTAPI RPC_STATUS RPC_ENTRY UuidCreate (OUT UUID RPC_FAR * Uuid); |
Структура возвращаемого параметра Uuid (guiddef.h):
typedef struct _GUID { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[ 8 ]; } GUID; typedef GUID UUID; |
UUID-идентификаторы часто записывают в виде текстовой строки:
{G1G2G3G4-G5G6-G7G8-G9G10-G11G12G13G14G15G16}
где Gx — значение соответствующего байта структуры в шестнадцатеричном представлении:
Data1 = G4G3G2G1
Data2 = G6G5
Data3 = G8G7
Data4 = G9G10G11G12G13G14G15G16
Согласно [1], первые два старших бита G9 определяют формат, в рамках которого необходимо интерпретировать значение идентификатора. В случае UuidCreate, они всегда равны 10, следовательно, формат должен полностью отвечать требованиям RFC4122, что даёт нам право трактовать четыре старших бита G7 как номер версии алгоритма. Наша подопытная выставляет в них последовательность 0100 (версия 4, «The randomly or pseudo-randomly generated version»), поэтому оставшиеся 122 бита структуры должны заполняться случайным образом. Так ли это на самом деле — сейчас узнаем.