Статья ASM – CNG (часть 1). Модуль криптографии нового поколения

Windows Vista взошла на трон в 2007-ом, но спустя буквально 1.5 года была с позором свергнута общественными критиками. Уныло шагая прочь, она с тоскою поглядывала на кричащее вслед сообщество, которое даже не успело должным образом оценить все её достоинства. Злую шутку с Вистой сыграл второпях наляпанный интерфейс, а ведь система подряжалась прийти к нам не с пустыми руками и хотела представить миру массу новых разработок, которые мы всё-таки взяли на вооружение, и пользуемся ими по сей день.

Одним из новшеств стала "криптография нового поколения" под названием CNG, или Cryptorgaphy Next Generation. Инфраструктура была призвана заменить отживший свой век на XP интерфейс CAPI (Crypto-API), оставив его лишь для совместимости. Входящими в состав CNG примитивами тут-же поспешили воспользоваться изголодавшиеся на тот момент сетевые протоколы SSL/TLS (Secure-Socket-Layer, и Transport-Layer-Secure), и как следствие весь зоопарк интернет-браузеров. На особом месте стоит и герой этой статьи – метод шифрования с аутентификацией, реализованный в современной линейке Win алгоритмом AES в режиме GCM. Предлагаю посмотреть на CNG и AES со-всевозможных ракурсов, а в некоторых случаях и под микроскопом.


Содержание:


1. Базовые сведения;
2. Симметричные и асимметричные алгоритмы;
3. Примитивы CNG из библиотеки bcrypt.dll;
4. Алгоритм AES и режимы его работы;
5. Практика – сбор информации об алгоритмах CNG;
6. Заключение.
-------------------------------------------------------


1. Базовые сведения

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

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


AlgoClass.png


Операционные системы начиная с Win7 вполне самодостаточны и не требуют для криптографии библиотек сторонних разработчиков. В то-же время, в некоторых компьюнити советуют использовать для этих целей левые модули типа OpenSSL, Crypto++, Sodium и прочие. Можно предположить, что подобные настроения возникают у программистов в большей степени из-за того, что они просто не могут разобраться с функциями библиотеки CNG bcrypt.dll, которая идёт в штатной поставке Win. И это не спроста, т.к. одни и те-же функции данной либы поддерживают работу как в пользовательском режиме, так и в режиме ядра. То-есть это библиотека низкого уровня и вести с ней диалог нужно только на лексиконе Unicode, поскольку ядро в упор не понимает строк ANSI. Благо всё уже прописано в сишном хидере bcrypt.h и нам остаётся лишь скопипастить его в свой инклуд (см.скрепку в подвале).

Эти и другие мелочи требуют особого внимания, за что система щедро отблагодарит нас мощной поддержкой шифрования, начиная от обычного DES и заканчивая современным AES/GCM с кодами аутентификации МАС. Если разобраться со-всеми нюансами, то можно будет с лёгкостью писать стиллеры паролей современных браузеров, расшифровывать перехваченные пакеты протоколов SSL/TLS, и многое другое. С приходом инфраструктуры CNG, алгоритм AES в режиме счётчика-Галуа (gcm) используется повсеместно, поскольку сочетает в себе как шифрование данных 32-байтным ключом (256-бит), так и проверку целостности этих данных на стороне получателя.


2. Симметричные и асимметричные алгоритмы

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

Поставщики криптографических услуг Win-CSP (Cryptography Service Provider), реализуют симметричное шифрование посредством всего трёх алгоритмов – это AES, DES и RC2/4. Так выглядит типичная схема обмена зашифрованными сообщениями:


Symmetric.png


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

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

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

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

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


Asymmetric.png


Системные провайдеры Win из асимметричных поддерживают три алгоритма – это ECC (Elliptic Сurve Сryptosystem, крипт на эллиптических кривых), RSA и DH. Аббревиатуры двух последних взяты по именам их создателей (видимо сильно любили себя шельмы) – это Rivest-Shamir-Adelman, и Diffie-Hellman соответственно. Если посмотреть на реалии, то выходит, что для асимметричного шифрования используется только RSA, а остальные – для тайной передачи ключей на расстояния "Secret Agreement", и оформлении цифровой подписи данных "Signature".

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


3. Примитивы CNG из библиотеки Вcrypt.dll

Подсистема криптографии CNG в современных ОС устроена так, что глобальным движком является библиотека BCRYPTPRIMITIVE.DLL (см.папку system32). Если заглянуть в её экспорт, можно обнаружить там всего шесть функций, при помощи которых она запрашивает соответствующие интерфейсы ядра (имена всех функций начинаются с префикса Get и заканчиваются суффиксом Interface). На более дружелюбном к юзеру высоком уровне расположилась либа Bcrypt.dll, в тушке которой сосредоточены уже сами функции на все случаи жизни. Поскольку они являются отдельными элементами глобальной системы криптографии, их назвали "примитивами". Не сказать, что таких примитивов много – в экспорте Bcrypt.dll на моей Win7 зарегистрировано всего 54 функций, большая часть из которых если и применяется, то только в клинических случаях.


Код:
Disassembly of File: bcrypt.dll
Code Offset = 00000400, Code Size = 00012C00
Data Offset = 00013000, Data Size = 00000600

Number of Objects = 0004 (dec), Imagebase = 6D800000h

   Object01: .text    RVA: 00001000  Offset: 00000400  Size: 00012C00  Flags: 60000020
   Object02: .data    RVA: 00014000  Offset: 00013000  Size: 00000600  Flags: C0000040
   Object03: .rsrc    RVA: 00015000  Offset: 00013600  Size: 00000600  Flags: 40000040
   Object04: .reloc   RVA: 00016000  Offset: 00013C00  Size: 00000600  Flags: 42000040

+++++++++++++++++++ EXPORTED FUNCTIONS ++++++++++++++++++
Number of Exported Functions = 0054 (decimal)

 Addr:6D8095B5 Ord:   1 (0001h) Name: BCryptAddContextFunction
 Addr:6D809B9F Ord:   2 (0002h) Name: BCryptAddContextFunctionProvider
 Addr:6D802391 Ord:   3 (0003h) Name: BCryptCloseAlgorithmProvider
 Addr:6D8092D6 Ord:   4 (0004h) Name: BCryptConfigureContext
 Addr:6D80985F Ord:   5 (0005h) Name: BCryptConfigureContextFunction
 Addr:6D808ED7 Ord:   6 (0006h) Name: BCryptCreateContext
 Addr:6D801B93 Ord:   7 (0007h) Name: BCryptCreateHash
 Addr:6D8018B8 Ord:   8 (0008h) Name: BCryptDecrypt
 Addr:6D809015 Ord:   9 (0009h) Name: BCryptDeleteContext
 Addr:6D8055C9 Ord:  10 (000Ah) Name: BCryptDeriveKey
 Addr:6D804A4B Ord:  11 (000Bh) Name: BCryptDeriveKeyCapi
 Addr:6D8088EE Ord:  12 (000Ch) Name: BCryptDeriveKeyPBKDF2
 Addr:6D801A5F Ord:  13 (000Dh) Name: BCryptDestroyHash
 Addr:6D801F40 Ord:  14 (000Eh) Name: BCryptDestroyKey
 Addr:6D805438 Ord:  15 (000Fh) Name: BCryptDestroySecret
 Addr:6D80528E Ord:  16 (0010h) Name: BCryptDuplicateHash
 Addr:6D8086A0 Ord:  17 (0011h) Name: BCryptDuplicateKey
 Addr:6D80195C Ord:  18 (0012h) Name: BCryptEncrypt
 Addr:6D807F9D Ord:  19 (0013h) Name: BCryptEnumAlgorithms
 Addr:6D805C33 Ord:  20 (0014h) Name: BCryptEnumContextFunctionProviders
 Addr:6D805823 Ord:  21 (0015h) Name: BCryptEnumContextFunctions
 Addr:6D80913F Ord:  22 (0016h) Name: BCryptEnumContexts
 Addr:6D808216 Ord:  23 (0017h) Name: BCryptEnumProviders
 Addr:6D808E4E Ord:  24 (0018h) Name: BCryptEnumRegisteredProviders
 Addr:6D8050BF Ord:  25 (0019h) Name: BCryptExportKey
 Addr:6D805737 Ord:  26 (001Ah) Name: BCryptFinalizeKeyPair
 Addr:6D801B44 Ord:  27 (001Bh) Name: BCryptFinishHash
 Addr:6D802206 Ord:  28 (001Ch) Name: BCryptFreeBuffer
 Addr:6D801E2E Ord:  29 (001Dh) Name: BCryptGenRandom
 Addr:6D80568E Ord:  30 (001Eh) Name: BCryptGenerateKeyPair
 Addr:6D801FBC Ord:  31 (001Fh) Name: BCryptGenerateSymmetricKey
 Addr:6D80307D Ord:  32 (0020h) Name: BCryptGetFipsAlgorithmMode
 Addr:6D801CA7 Ord:  33 (0021h) Name: BCryptGetProperty
 Addr:6D801B0B Ord:  34 (0022h) Name: BCryptHashData
 Addr:6D804302 Ord:  35 (0023h) Name: BCryptImportKey
 Addr:6D804EB8 Ord:  36 (0024h) Name: BCryptImportKeyPair
 Addr:6D802C72 Ord:  37 (0025h) Name: BCryptOpenAlgorithmProvider
 Addr:6D80940A Ord:  38 (0026h) Name: BCryptQueryContextConfiguration
 Addr:6D8099C9 Ord:  39 (0027h) Name: BCryptQueryContextFunctionConfiguration
 Addr:6D80A06D Ord:  40 (0028h) Name: BCryptQueryContextFunctionProperty
 Addr:6D8059DC Ord:  41 (0029h) Name: BCryptQueryProviderRegistration
 Addr:6D80359F Ord:  42 (002Ah) Name: BCryptRegisterConfigChangeNotify
 Addr:6D80A41D Ord:  43 (002Bh) Name: BCryptRegisterProvider
 Addr:6D80970C Ord:  44 (002Ch) Name: BCryptRemoveContextFunction
 Addr:6D809D34 Ord:  45 (002Dh) Name: BCryptRemoveContextFunctionProvider
 Addr:6D8029C6 Ord:  46 (002Eh) Name: BCryptResolveProviders
 Addr:6D80551A Ord:  47 (002Fh) Name: BCryptSecretAgreement
 Addr:6D806009 Ord:  48 (0030h) Name: BCryptSetAuditingInterface
 Addr:6D809EC5 Ord:  49 (0031h) Name: BCryptSetContextFunctionProperty
 Addr:6D8020D4 Ord:  50 (0032h) Name: BCryptSetProperty
 Addr:6D8084B3 Ord:  51 (0033h) Name: BCryptSignHash
 Addr:6D804609 Ord:  52 (0034h) Name: BCryptUnregisterConfigChangeNotify
 Addr:6D808D52 Ord:  53 (0035h) Name: BCryptUnregisterProvider
 Addr:6D8060FA Ord:  54 (0036h) Name: BCryptVerifySignature

SysCNG.png



4. Алгоритм шифрования AES, и режимы его работы

AES (Advanced Encryption Standard) – это расширенная версия устаревшего DES. Алгоритм представляет собой симметричный блочный шифр. Такое определение означает, что на вход процедуры шифрования данных, информация должна поступать блоками. Размер одного блока для AES всегда равен 16-байт, в виде матрицы 4х4. По этой причине, блочные шифры называют ещё "матричными". AES увеличивает кодируемую информацию и делает её кратным размеру блока. Однако при использовании режима "GCM" размер остаётся прежним – сколько байт забросим в печь, столько-же и получим с выхлопной трубы.

Чтобы после процедуры крипта была возможность обратно восстановить информацию, алгоритму требуется ключ-шифрования. Разрядность ключа указывается в названии алгоритма, например AES-128, 192 или 256 бит. Другие размеры ключей AES не поддерживает, так-что мы ограничены ключами: 128/8=16 байт, 192/8=24 байта, и 256/8=32 байта. Разница между зоопарком AES заключается только в размере ключа и кол-ве используемых повторений (раундов), но не в размере блока – блок всегда одинаковый 16-байт. Счётчик раундов составляет 10,12,14 итераций соответственно.

В наиболее простом и интуитивно понятном классическом режиме "ECB" (Electronic Codebook), сложным преобразованиям подвергается каждый блок данных, при этом такая операция повторяется над каждым блоком несколько раз, что подразумевает собой "раунд". Главный недостаток режима ЕСВ заключается в том, что одинаковые блоки исходных данных (например цепочка из 32-х и более нулей) при шифровании дадут одинаковые блоки шифротекста – это существенно содействует взлому. Поэтому режим ECB не рекомендуется использовать при шифровании текстов, по длине превышающих один 16-байтный блок.

Благодаря этой проблеме, на свет появился ещё один режим AES под кличкой "CBC" (Сipher Block Chaining). Здесь уже каждый последующий блок шифруется выходным значением предыдущего, что связывает блоки между собой образуя цепочку "Chain". При такой схеме возникает резонный вопрос: -"Если следующий блок кодируется предыдущим, то чем тогда кодировать самый первый блок, ведь у него нет предыдущего?".

Для этого, режим CBC требует на входе ещё одно значение, которое назвали "вектор инициализации" IV (Initialization Vector). В зависимости от режима работы (а кроме CBC есть ещё и масса других), размер IV может варьироваться в диапазоне от 12 до 16-байт, не превышая размер блока AES. Как-правило, его значение программист должен сгенерировать рандомно, на подготовительном этапе шифрования. Вот как выглядят эти режимы в графике (Plain открытый, Cipher зашифрованный текст):


AES-EBC.png


Глядя на эту схему становится очевидно, что режим электронной книжки ECB отжил уже свой век, и на практике не рекомендуется к использованию. В случае со-вторым вариантом CBC, мы сталкиваемся с необходимостью передавать получателю вместе с зашифрованными данными ключ, и значение вектора IV – иначе декодер на приёмном узле просто не сможет расшифровать закодированную информацию. Думаю не имеет смысла упоминать о том, что будет, если по дороге "потеряется" какой-нибудь промежуточный блок – чинить уже будет нечего, т.к. все блоки связаны между собой неразрывными узами.

Алгоритм AES оказался настолько удачным, что разработчики начали комбинировать различные схемы. К примеру ещё одной вариацией режима CBC стал модернезированный "CFB" (Feed-Back, обратная связь). Здесь, сначала шифруется вектор IV, который на выходе складывается ксором XOR со входным блоком данных. Весьма интересная схема, и вполне может занять своё место на практике:


AES-CFB.png



4.1. AES в режиме цепочки и счётчика

Следующим шагом к достижению гармонии было внедрение в алгоритм AES внутреннего счётчика – режим получил название "CTR" (Counter). Это самый распространённый вариант реализации AES из-за своей крипткостойкости ко взлому. Не смотря на то, что здесь нет связывания блоков между собой, каждый последующий блок всё-равно шифруется новым значением IV, поскольку при переходе к следующему блоку счётчик автоматически увеличивается на 1. Счётчик вектора IV имеет размер 4-байта, и на старте шифрования сбрасывается алгоритмом в нуль.


AES-CTR.png


Важно! Обратите внимание, что в режимах без счётчика, значение вектора IV должно совпадать с размером 16-байтного блока AES. Это обязательное требование, иначе функция шифрования BCryptEncrypt() будет возвращать ошибку "INVALID_PARAMETR". При этом в стиле Microsoft, какой именно параметр из 10-ти возможных не указывается, что затрудняет поиск проблемы.

Если-же мы выбрали режим шифрования AES со-счётчиком (CTR, CCM, GCM), то вектор IV должен быть размером уже 12-байт, поскольку 4-байтный внутренний счётчик фактически является частью IV, занимая в нём младшие 4-байта из 16-ти. Не забывайте об этом!


5. Практика – сбор информации о провайдерах CNG

Теперь, когда мы получили дозу фундаментальных основ, можно рассмотреть несколько функций из Bcrypt.dll. Начнём с того, что перечислим все поддерживаемые стандартным поставщиком Win-CNG алгоритмы, ведь должны-же мы знать, какие из них доступны нам для использования в своих программах. Для этого предусмотрена функция BCryptEnumAlgorithm() с таким прототипом:


C-подобный:
NTSTATUS BCryptEnumAlgorithms  ;//<---- 0 = OK!
  dwAlgOperations   dd  0  ;// тип операции
  pAlgCount         dd  0  ;// кол-во элементов в массиве ppAlgList
  ppAlgList         dd  0  ;// указатель на BCRYPT_ALGORITHM_IDENTIFIER
  dwFlags           dd  0  ;// резерв

;//********************************
;//  Operations type flags

 BCRYPT_CIPHER_OPERATION                  = 0x00000001   ;// алго симметричного шифрования
 BCRYPT_HASH_OPERATION                    = 0x00000002   ;// хеширование
 BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION   = 0x00000004   ;// асимметричные
 BCRYPT_SECRET_AGREEMENT_OPERATION        = 0x00000008   ;// передача ключей
 BCRYPT_SIGNATURE_OPERATION               = 0x00000010   ;// цифровая подпись
 BCRYPT_RNG_OPERATION                     = 0x00000020   ;// генератор случайных чисел (рандом)
 BCRYPT_KEY_DERIVATION_OPERATION          = 0x00000040   ;//<---- начиная с Win-8

;//********************************
struct BCRYPT_ALGORITHM_IDENTIFIER
  pszName           dd  0  ;// линк на Юникод-имя алгоритма
  dwClass           dd  0  ;// класс/ID алгоритма
  dwFlags           dd  0  ;// резерв
ends

Функция возвращает массив Unicode-строк с именами алгоритмов. Например, если хотим перечислить все алго симметричного шифрования, то передаём функции в первом аргументе константу(1) "BCRYPT_CIPHER_OPERATION". Если интересуют алго хеширования данных, то соответственно "BCRYPT_HASH_OPERATION" и т.д. Кол-во записей в полученном массиве функция возвращает в переменную, на которую указывает второй параметр.

Поскольку константы операций для первого аргумента имеют значение степени(2), это способствует организации цикла. То-есть отправляем в первый аргумент единицу, и на следующей итерации, сдвигом влево умножаем предыдущее значение на 2. Как видим, всего итераций в цикле должно быть 6 (последний с константой 40h моя семёрка не поддерживает). Далее просто берём из структуры "BCRYPT_ALGORITHM_IDENTIFIER" первое поле, и получаем указатель на цепочку имён алгоритмов.

Чтобы добавить в программу красок и повысить её инфо-нагрузку, можно вывести на консоль детали каждого из алгоритмов. От вызова предыдущей функции мы уже имеем имя алгоритма шифрования, и достаточно будет открыть его функцией BCryptOpenAlgorithmProvider(), после чего запросить требуемые свойства через BCryptGetProperty().


C-подобный:
NTSTATUS BCryptOpenAlgorithmProvider
 phAlgorithm           dd  0   ;// сюда вернётся дескриптор алгоритма
 pszAlgId              dd  0   ;// указатель на Unicode-имя алго (у нас он есть)
 pszImplementation     dd  0   ;// 0 (поставщик в дефолте)
 dwFlags               dd  0   ;// 0

NTSTATUS BCryptGetProperty  ;//<---- 0 = OK!
 hObject        dd  0   ;// дескриптор алгоритма от fn.выше
 pszProperty    dd  0   ;// линк на Unicode-имя свойства
 pbOutput       dd  0   ;// адрес буфера, который получает значение свойства
 cbOutput       dd  0   ;// размер буфера pbOutput
 pcbResult      dd  0   ;// кол-во байтов, скопированных в буфер pbOutput
 dwFlags        dd  0   ;// резерв

Вторая функция во-втором параметре ожидает указатель на имя свойства, которое мы планируем получить – это текстовые Unicode-строки (см.инклуд в скрепке). В данном случае речь идёт про симметричные алгоритмы, и меня будут интересовать всего три их свойства: (1) размер блока шифрования данных, (2) размер ключа, и прицепом (3) размер временного объекта. Объекты подобного рода, функции используют для своих производственных нужд и в некоторых случаях мы должны выделять под них память (например при хешировании). Так-что инфа об объектах явно не будет лишней.

C-подобный:
;// BCryptGetProperty strings (Set только для CHAINING_MODE)
;// https://docs.microsoft.com/en-us/windows/win32/seccng/cng-property-identifiers

 BCRYPT_ALGORITHM_NAME          du  'AlgorithmName',0         ;// возвращает юникоде-имя алго
 BCRYPT_AUTH_TAG_LENGTH         du  'AuthTagLength',0         ;// тег аутентификации в структуру (только алгоритмы)
 BCRYPT_BLOCK_LENGTH            du  'BlockLength',0           ;// DWORD - только алгоритмы блочного шифрования
 BCRYPT_BLOCK_SIZE_LIST         du  'BlockSizeList',0         ;// массив DWORD, число элементов = /4
 BCRYPT_CHAINING_MODE           du  'ChainingMode',0          ;// режим алгоритма AES
 BCRYPT_DH_PARAMETERS           du  'DHParameters',0          ;// BCRYPT_DH_PARAMETER_HEADER struct (ключ Диффи-Хеллмана)
 BCRYPT_DSA_PARAMETERS          du  'DSAParameters',0         ;// BCRYPT_DSA_PARAMETER_HEADER struct
 BCRYPT_EFFECTIVE_KEY_LENGTH    du  'EffectiveKeyLength',0    ;// DWORD - длина ключа RC2
 BCRYPT_HASH_BLOCK_LENGTH       du  'HashBlockLength',0       ;// DWORD - применимо только к хеш-алгоритмам
 BCRYPT_HASH_LENGTH             du  'HashDigestLength',0      ;// DWORD - размер в байтах хеш-значения поставщика
 BCRYPT_HASH_OID_LIST           du  'HashOIDList',0           ;// BCRYPT_OID_LIST struct
 BCRYPT_INITIALIZATION_VECTOR   du  'IV',0                    ;// содержит IV, применяется только к ключам
 BCRYPT_KEY_LENGTH              du  'KeyLength',0             ;// DWORD - размер в битах симметричного ключа
 BCRYPT_KEY_LENGTHS             du  'KeyLengths',0            ;// BCRYPT_KEY_LENGTHS_STRUCT - размеры ключей алгоритма
 BCRYPT_KEY_STRENGTH            du  'KeyStrength',0           ;// DWORD - размер ключа в битах
 BCRYPT_MESSAGE_BLOCK_LENGTH    du  'MessageBlockLength',0    ;// применимо к режиму CFB (=1 для 8-битного CFB)
 BCRYPT_MULTI_OBJECT_LENGTH     du  'MultiObjectLength',0     ;// BCRYPT_MULTI_OBJECT_LENGTH_STRUCT (найти размер буфера)
 BCRYPT_OBJECT_LENGTH           du  'ObjectLength',0          ;// DWORD - размер в байтах подобъекта поставщика
 BCRYPT_PADDING_SCHEMES         du  'PaddingSchemes',0        ;// схема заполнения алгоритма RSA
 BCRYPT_PROVIDER_HANDLE         du  'ProviderHandle',0        ;// DWORD - дескриптор создавшего объект поставщика
 BCRYPT_SIGNATURE_LENGTH        du  'SignatureLength',0       ;// DWORD - размер в байтах длины подписи ключа

Функция BCryptGetProperty() возвращает размер блока и размер временного объекта в виде значений DWORD. А чтобы получить свойства ключа шифрования, нужно подготовить структуру такого характера. Выше упоминалось, что ключи у алгоритмов могут быть разной разрядности, например для AES это: 128, 192 и 256 бит. В структуре ниже, этот диапазон хранится как мин/макс значение ключа, и шаг прироста в виде поля "Increment".

C-подобный:
struct BCRYPT_KEY_LENGTHS_STRUCT
   dwMinLen        dd  0   ;// мин.значение ключа в битах
   dwMaxLen        dd  0   ;// макс.значение ключа
   dwIncrement     dd  0   ;// шаг ключа в битах
ends

В своём примере, сначала я запрашиваю тип операции = "симметричное шифрование", после чего перечисляю все алгоритмы, которые способны осуществить эту операцию. На следующем этапе, меняю тип на "хеширование данных" (умножаю предыдущее значение на 2) и так-же перечисляю её алгоритмы. В коде таких повторов 6, по числу поддерживаемых поставщиком Win криптографических действий. В самом конце я опять возвращаюсь к первому пункту "симметричное шифрованию", и вывожу уже детали каждого из алгоритмов. Это поможет прояснить общую картину инфраструктуры CNG:

C-подобный:
format pe console
entry  start
;//------------
section '.inc' data readable
include 'win32ax.inc'
include 'equates\bcrypt.inc'
;//------------
.data
algCount     dd  0
operation    dd  1  ;// начинаем с "BCRYPT_CIPHER_OPERATION = 1"

algHndl      dd  0
pLen         dd  0
objLen       dd  0
pcbResult    dd  0

align 16
algList      BCRYPT_ALGORITHM_IDENTIFIER
keyLen       BCRYPT_KEY_LENGTHS_STRUCT

buff         db  0
;//------------
.code
start:   invoke  SetConsoleTitle,<'*** CNG Enum algo ***',0>

        cinvoke  printf,<10,' Enum CNG Algorithm',\
                         10,' ***************************',\
                         10,'   Symmetric cipher....:  ',0>
         invoke  BCryptEnumAlgorithms,[operation],algCount,algList,0
         call    GetAlgoName

         shl     [operation],1
        cinvoke  printf,<10,'   Hash algorithm......:  ',0>
         invoke  BCryptEnumAlgorithms,[operation],algCount,algList,0
         call    GetAlgoName

         shl     [operation],1
        cinvoke  printf,<10,'   Asymmetric cipher...:  ',0>
         invoke  BCryptEnumAlgorithms,[operation],algCount,algList,0
         call    GetAlgoName

         shl     [operation],1
        cinvoke  printf,<10,'   Secret agreement....:  ',0>
         invoke  BCryptEnumAlgorithms,[operation],algCount,algList,0
         call    GetAlgoName

         shl     [operation],1
        cinvoke  printf,<10,'   Signature...........:  ',0>
         invoke  BCryptEnumAlgorithms,[operation],algCount,algList,0
         call    GetAlgoName

         shl     [operation],1
        cinvoke  printf,<10,'   Random generator....:  ',0>
         invoke  BCryptEnumAlgorithms,[operation],algCount,algList,0
         call    GetAlgoName

;// *********************************************
        cinvoke  printf,<10,10,' Symmetric algo info',\   ;//<----- детали симметричных алгоритмов!
                         10,' ***************************',0>

         invoke  BCryptEnumAlgorithms,BCRYPT_CIPHER_OPERATION,algCount,algList,0
         call    GetAlgoProperty

@exit:  cinvoke  _getch
        cinvoke  exit,0
;//------------

;//----- ВСПОМОГАТЕЛЬНЫЕ ПРОЦЕДУРЫ ------------//
proc GetAlgoName                  ;//<---- выводит на консоль массив имён алгоритмов
         mov     ecx,[algCount]
         mov     esi,[algList.pszName]
         mov     esi,[esi]
@@:      push    esi ecx
        cinvoke  printf,<'%ls, ',0>,esi
         pop     ecx esi
@01:     lodsw
         cmp     ax,0
         jne     @01
         loop    @b
         ret
endp
;//------------

proc GetAlgoProperty              ;//<---- запрашивает свойства по имени
         mov     ecx,[algCount]
         mov     esi,[algList.pszName]
         mov     esi,[esi]
@cycle:  push    esi ecx esi
        cinvoke  printf,<10,10,' %ls *********',0>,esi

;// Открыть алгоритм по имени (указатель на имя лежит в ESI)
         pop     esi
         invoke  BCryptOpenAlgorithmProvider,algHndl,esi,0,0

;// Размер блока шифрования
         invoke  BCryptGetProperty,[algHndl],BCRYPT_BLOCK_LENGTH,pLen,4,pcbResult,0
        cinvoke  printf,<10,'    Block size...:  %d byte',0>,[pLen]

;// Диапазон размеров ключей (и их шаг)
         invoke  BCryptGetProperty,[algHndl],BCRYPT_KEY_LENGTHS,keyLen,12,pcbResult,0
         mov     eax,[keyLen.dwMinLen]
         mov     ebx,[keyLen.dwMaxLen]
         mov     ecx,[keyLen.dwIncrement]
        cinvoke  printf,<10,'    Key length...:  %03d...%03d bit, increment = %d bit',0>,\
                                 eax,ebx,ecx

;// Размер временного буфера под данные
         invoke  BCryptGetProperty,[algHndl],BCRYPT_OBJECT_LENGTH,objLen,4,pcbResult,0
        cinvoke  printf,<10,'    Temp object..:  %d byte',0>,[objLen]

         pop     ecx esi
@@:      lodsw
         cmp     ax,0
         jne     @b
         dec     ecx
         jnz     @f
         jmp     @stop
@@:      jmp     @cycle
@stop:   ret
endp
;//------------
section '.idata' import data readable
library  kernel32,'kernel32.dll',msvcrt,'msvcrt.dll',bcrypt,'bcrypt.dll'

include  'api\kernel32.inc'
include  'api\msvcrt.inc'
include  'api\bcrypt.inc'

Win_algo.png


Таким образом нам удалось получить неплохую голограмму инфраструктуры CNG, и это данные лишь одной из операций – симметричное шифрование. Лог в очередной доказывает, что алгоритм AES имеет размер входного блока 16-байт, и поддерживает три типа ключей: 128, 192 и 256 бит.

Зато его предшественник DES всех мастей, оперирует блоками по 8-байт, с фиксированными размерами ключей. Если классический DES производит над каждым своим блоком всего одну операцию шифрования, то 3DES повторяет её трижды (см.раунды).

А вот алгоритмы RC2 и RC4 в корень отличаются друг от друга, хотя и посещают одну синагогу. RC2 это блочный симметричный шифр с диапазоном ключей от 40 до 128 бит (с шагом в байт), в то время как RC4 относится к потоковым шифрам, т.к. он оперирует не блоками данных, а процеживает информацию по-байтно, шифруя на уровне бит.

Алгоритмы SHA и MD для хеширования – это стандартная практика. А вот то-что появился для этих целей AES-GMAC – это уже гуд. Что это такое мы обсудим в следующей части статьи, но если-бы нам приспичило получить хеш AES/GMAC на системе Win-XP, то пришлось-бы звать на помощь библиотеки сторонних разработчиков, такие как Crypto++. Начиная-же с Win-Vista данный алгоритм уже включён в состав ОС, и необходимость грузить непонятно кем написанные либы сама-собой отпадает.

Из асимметричных алго маячит только RSA, а больше нам и не надо – в этом сегменте RSA занимает позицию лидера. Кроме того имеем широкий выбор для создания цифровых подписей "Signature", и базовую тройку для генерации случайных чисел – это RNG (RandomGen мастдая), алгоритм одобренный институтом FIPS (Federal Information Processing Standard Publication), и DUAL_EC_RNG, что подразумевает "детерминированный генератор случайных бит с двойной эллиптической кривой".


6. Заключение

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

В следующей части запланировано несколько пруфов, которые докажут всё выше сказанное. Мы более подробно разберём современный и широко распространённый вид шифрования с тегами аутентификации AES-256 в режиме "GCM", узнаем что такое AEAD и в каком виде передаются зашифрованные данные, чтобы их можно было расшифровать. К примеру браузеры Chrome версии 80 и выше шифруют пароли юзеров и кукисы именно в режиме AES/GCM и мы убедимся, как легко их можно будет поиметь.

В скрепку положил инклуд для работы с библиотекой CNG на фасме,
а так-же исполняемый файл для вывода лога системного поставщика. На этом занавес – пока!
 

Вложения

  • BCrypt_CNG.zip
    8,1 КБ · Просмотры: 352
Последнее редактирование:

w2ll3t0Rl1v3

Green Team
22.01.2021
14
4
BIT
0
Спасибо что разжевал, прочитал. что-то понял....сохраню и перечитаю позже ещё раз
 

Evgeny D

Green Team
04.07.2021
31
7
BIT
184
Добрый день!
Спасибо за интересные статьи, хотел узнать есть возможность ваши статьи в pdf скачать?
 

Marylin

Mod.Assembler
Red Team
05.06.2019
320
1 430
BIT
588
Привет!
В формате pdf у меня их нет, я пишу в ворде.
 

Evgeny D

Green Team
04.07.2021
31
7
BIT
184
В ворде тоже хорошо

Выложите куда-нибудь пожалуйста или здесь прикрепите
 

Mikl___

Green Team
01.09.2019
30
49
BIT
104
Evgeny D,
то есть самостоятельно перевести из HTML в pdf это для вас слишком сложно?
negative[1].gif
 

Evgeny D

Green Team
04.07.2021
31
7
BIT
184
Mikl Совсем нет, дело в том, что корректно не сохраняется, в том числе вкладки.
Просьба была адресована автору.
 

Andrei_0x7eff0

Green Team
23.09.2023
29
1
BIT
207

Модуль криптографии нового поколения. Автор пишет, я покажу как легко их можно будет поимет. Может это уже старого поколения.​

 

Marylin

Mod.Assembler
Red Team
05.06.2019
320
1 430
BIT
588
Может это уже старого поколения.
Так вроде после bcrypt.dll нет ничего в штатной поставке Win, или вы знаете что-то свежее?
Хотя да.. и она уже пахнет нафталином, т.к. Виста давно канула в лету, а в Win10 пока ничего не подвезли.
В лучшем случае какой-нибудь новый провайдер CSP появился - будет повод проверить.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!