HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > БЕЗОПАСНОСТЬ И УЯЗВИМОСТИ > Безопасность и Анонимность > Защита ОС: вирусы, антивирусы, файрволы.
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 04.07.2021, 18:20
Marylin
Постоянный
Регистрация: 01.09.2019
Сообщений: 378
С нами: 3526561

Репутация: 0
По умолчанию

Это вторая часть разговора об инфраструктуре шифрования нового поколения "Crypt Next Generation". Впервоймы рассмотрели базовые сведения о шифровании данных, и режимы работы алгоритма AES. В том-что это действительно мотор поколения Next можно убедиться на сайте MSDN, где на невинный запрос описания какой-либоустаревшей функциииз библиотеки CAPI, мелкософт встречает нас грозным предупреждением типа: -"Этот API устарел! Новое программное обеспечение должно использовать CNG, т.к. Microsoft может удалить этот API в будущих выпусках". Таким образом нас просто ставят перед фактом, подталкивая на изучение современных методов. Так не будем-же противиться этому..

Содержание:


1. Хеширование информации;
2. AAD – дополнительные данные для хеша;
3. Алгоритм AES в режиме GCM;
4. Практика – шифрование с аутентификацией;
5. Использование AEAD в браузерах;
6. Выводы.
-------------------------------------------------------

1. Хеширование данных

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

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

Вне зависимости от размера информации на входе, мы всегда получаем хеш фиксированной длины. К примеру алгоритмы MD2[4,5] генерируют на выходе 16-байтные значения, а разрядность хеша SHA (Secure Hash Algorithm) указывается в его названии, т.е. для SHA-512 будет соответственно 512-бит или 64 байта (особняком стоит SHA1 с размером хеша 160-бит = 20 байт).

Возьмём к примеру пароль из четырёх символов "1234". Как его хранить в программе? Ясно, что не в открытом виде, а если зашифровать обычным ксором, то подбор его брутфорсом займёт пару минут. Поэтому мы снимаем с пароля хеш например SHA-256 (sha2) и на выходе получаем уже 32-байтное значение этого пароля. Так решаются сразу две проблемы: во-первых автоматически отваливается брутфорс любого рода, а во-вторых – скрывается от посторонних глаз пароль. Получить коллизию при 32-байтном значении нереально, а потому можно быть уверенным, что хеши двух паролей не совпадут.

Для программного его расчёта нам понадобятся аж шесть функций из библиотеки CNG Bcrypt.dll, т.к. в нашем распоряжении не законченные функции, а их примитивы. Конечно удобней было-бы собрать все в один флакон и обозвать набор элементарно BCryptHash() (кстати подобная функция есть и добавлена она только в Win10), но мы имеем дело с библиотекой низкого уровня, где каждую единицу приходится обрабатывать отдельно – это раскрепощает нас, хотя и требует взамен больше телодвижений.

Функция BCryptOpenAlgorithmProvider() возвращает дескриптор выбранного алгоритма. Поскольку мы собираемся хешировать данные, то передаём этой функции во-втором параметре указатель на Unicode-строку "SHA256". Подготавливает окружение для операции следующий примитив BCryptCreateHash(). При вызове, она запросит у нас память для временного своего объекта, так-что перед ней нужно будет узнать у провайдера размер этого объекта для данного алгоритма, при помощи BCryptGetProperty() с аргументом "OBJECT_LENGTH".


Теперь можно приступать непосредственно к процессу хеширования BCryptHashData() – функция ожидает адрес исходных данных и их размер. Финализирует операцию BCryptFinishHash(), которая сохраняет готовый к употреблению хеш в указанный нами буфер. По окончании, временный объект и всё окружение подлежит уничтожению функцией BCryptDestroyHash(), но если это последнее, что делает наша программа, ExitProcess() на выходе сама подчистит все выделенные системой ресурсы.

В своей демке, я посчитаю хеш введённого юзером пароля сразу двумя алгоритмами SHA-256 и MD5, чтобы можно было сравнить их значения:


C-подобный:


Код:
format pe console
entry  start
;
//------------
section
'.inc'
data readable
include
'win32ax.inc'
include
'equates\bcrypt.inc'
;
//------------
.
data
pcbResult     dd
0
passLen       dd
0
hashBuff      rb
64
;
//
cinvoke  printf
,

;
// Получить дескриптор алгоритма SHA-512, и собрать о нём инфу
invoke  BCryptOpenAlgorithmProvider
,
shaAlgHndl
,
BCRYPT_SHA256_ALGORITHM
,
0
,
0
invoke  BCryptGetProperty
,
[
shaAlgHndl
]
,
BCRYPT_HASH_LENGTH
,
shaLen
,
4
,
pcbResult
,
0
invoke  BCryptGetProperty
,
[
shaAlgHndl
]
,
BCRYPT_OBJECT_LENGTH
,
shaObjLen
,
4
,
pcbResult
,
0
;
// Получить дескриптор MD5, и собрать о нём инфу
invoke  BCryptOpenAlgorithmProvider
,
md5AlgHndl
,
BCRYPT_MD5_ALGORITHM
,
0
,
0
invoke  BCryptGetProperty
,
[
md5AlgHndl
]
,
BCRYPT_HASH_LENGTH
,
md5Len
,
4
,
pcbResult
,
0
invoke  BCryptGetProperty
,
[
md5AlgHndl
]
,
BCRYPT_OBJECT_LENGTH
,
md5ObjLen
,
4
,
pcbResult
,
0
mov     eax
,
[
shaLen
]
;
// длина хеша SHA в байтах
mov     ebx
,
eax
         shl     ebx
,
3
;
// ..и в битах (х8)
cinvoke  printf
,

,
eax
,
ebx
,
[
shaObjLen
]
mov     eax
,
[
md5Len
]
mov     ebx
,
eax
         shl     ebx
,
3
cinvoke  printf
,

,
eax
,
ebx
,
[
md5ObjLen
]
;
//***********************************************
;
// Запрашиваем у юзера пасс в свой буфер
cinvoke  printf
,

cinvoke  scanf
,

,
buff

         invoke  lstrlen
,
buff
;
//
,
[
passLen
]
mov     ecx
,
[
shaLen
]
mov     esi
,
hashBuff
         call    PrintHexString
;
//********************
invoke  BCryptCreateHash
,
[
md5AlgHndl
]
,
md5Hndl
,
hashObject
,
[
md5ObjLen
]
,
0
,
0
,
0
invoke  BCryptHashData
,
[
md5Hndl
]
,
buff
,
[
passLen
]
,
0
invoke  BCryptFinishHash
,
[
md5Hndl
]
,
hashBuff
,
[
md5Len
]
,
0
invoke  BCryptDestroyHash
,
[
md5Hndl
]
cinvoke  printf
,

mov     ecx
,
[
md5Len
]
mov     esi
,
hashBuff
         call    PrintHexString

@exit
:
cinvoke  _getch
        cinvoke  exit
,
0
;
//------------
;
//----- Процедура вывода Hex-строки на консоль --------------
;
//----- на входе: ESI = указатель на данные, ECX = длина
proc PrintHexString
@@
:
xor     eax
,
eax
         lodsb
         push    ecx esi
        cinvoke  printf
,

,
eax
         pop     esi ecx
         loop    @b
         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'


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

2. AAD – дополнительные данные для хеша

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

Одним из разумных решений стало добавление запроса на "ввод пароля" от отправителя. Теперь, на этапе хеширования информации, отправитель должен предоставить функции не только сами данные, но и дополнительную текстовую строку, чтобы однозначно идентифицировать себя – эту строку назвали AAD, или "Additional Authenticated Data". AAD можно рассматривать как внешнюю соль к хешу, и чтобы значения двух хешей совпали на передающей и принимающей стороне, получатель должен подтвердить оригинальный AAD (т.е. ввести пароль).

Так появились хеши с аутентификацией. Их тут-же взяли на вооружения сразу несколько режимов шифрования AES, известных под общим именем AEAD – "Authenticated Encryption with Associated Data" (аутентифицированное шифрование с присоединёнными данными). В этих режимах, алгоритм сначала шифрует исходные данные, после чего сразу-же вычисляет с них хеш. В контексте шифрования, такие хеши назвали МАС, что в развёрнутом виде означает "Message Authentification Code", или код аутентификации сообщения.

Таким образом, AEAD-режимы сочетают в себе две операции: непосредственно шифрование для конфиденциальности, и вычисление MAC для проверки целостности данных, с аутентификацией юзера. Это приводит к более низким вычислительным затратам, по сравнению с использованием отдельных функций шифрование + аутентификация. Здесь, первая часть сообщения шифруется, вторая часть в виде AAD остаётся открытой, и всё сообщение целиком накрыто аутентификацией. В настоящее время имеются несколько AEAD-режимов AES, но в тройке лидеров: OCB2 (Offset codebook), CCM (CBC+MAC), и герой данного чтива GCM (Galois Counter Mode, CTR+GMAC) как наиболее популярный.

Один из примеров использования AAD – это когда наше приложение служит прокси-сервером для развёртывания интерфейса с одним симметричным ключом и неограниченным кол-вом клиентов (причём каждый клиент находится в своём сегменте безопасности). Например, приложение может быть базой, в которую юзеры заносят данные личного характера. Когда одному из них нужно просмотреть свои записи, приложение может затребовать уникальное его имя в качестве AAD. В этом сценарии, функция AES откажет в расшифровке данных, если для крипт/декрипта не используется одно и то-же значение AAD. Параметр является опциональным в функции шифрования BCryptEncrypt(), однако он всегда принимает участие в процессе – если мы игнорируем его, просто используется пустая строка.

3. Алгоритм AES в режиме GCM

На рисунке ниже представлена схема режима AES/GCM где видно, что он состоит из двух самостоятельных модулей – это шифрование и аутентификация (серый блок). Обратите внимание на локацию доп.данных AAD, обозначенных здесь как "Auth-Data". Они поступают в модуль GMAC и не шифруются, подвергаясь только операции хеш. Результатом функций AEAD является зашифрованный текст + тег аутентификации МАС (синие блоки). Вместе с ключом, вектором IV и необязательным AAD, 16-байтный тег МАС необходим для расшифровки закрытого CipherText. Криптографы всегда рекомендуют использовать режимы AEAD для блочных алгоритмов шифрования с симметричным ключом:




Если присмотреться к этой схеме, то режим AES/GMAC есть ничто-иное как расширенная версия режима CTR(см.предыдущую часть статьи), к которому добавлен модуль GMAC для одновременного вычисления хеша:



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

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

Чтобы увеличить скорость шифрования, начиная с процессоров "Xeon-56xx" Intel ввела специальный набор инструкций под названием AES-NI (New Instruction). Так появилась
аппаратная поддержкашифрования AES и хеширования SHA. Несмотря на то, что в данный набор входят всего несколько инструкций, они относятся к разряду SIMD, когда одна инструкция выполняет сразу несколько микроопераций (Single Instruction, Multiple Data). Благодаря тому, что каждая такая инструкция проделывает свою часть глобальной работы (AES включает в себя 4-этапа) – это позволило добиться 5-кратного увеличения скорости. Таким образом, если вам нужен быстрый режим шифрования с массой дополнений, используйте AES/GCM.

Типичный интерфейс его программирования выглядит так:

• Шифрование

- Вход: открытый текст + ключ + IV + опционально AAD в виде открытого текста, который не будет зашифрован, но будет защищён аутентификацией.
- Выход: зашифрованный текст + тег аутентификации МАС.

• Расшифровка
- Вход: зашифрованный текст + ключ + IV + МАС + AAD (если он использовался во время шифрования).
- Выход: открытый текст или ошибка, если МАС или AAD не соответствуют зашифрованному тексту.

Кстати в литературе, мнение криптографов на счёт определения "вектор инициализации IV" расходятся. Дело в том, что термин применяется как в контексте шифрования, так и в руководствах по хешированию данных. Поэтому (если дотошно придираться к мелочам), здесь нужно внести ясность..

Для хэш-функций, вектор инициализации – это постоянная константа, которая никогда не меняется (полином, см.в
описание MD5). Для блочных-же шифров, одним из обязательных условий является уникальный IV для каждого блока AES – такие векторы назвали Nonce, или одноразовое не повторяющееся значение
Код:
N(once)
. Это вносит путаницу и делает свойства двух терминов неоднозначными. Но т.к. IV в промышленных процедурах хеширования зарыт глубоко внутри и его хвост не торчит снаружи, то им можно пренебречь. Таким образом остаётся только шифрование, где термины IV/nonce превращаются в синонимы, даже на уровне документации MSDN.

4. Практика – шифрование AES без аутентификации

Теперь к практической реализации AES..

Говорят, что качество имеет различные метрики, и это утверждение как-нельзя лучше подходит к выбору нами алгоритма шифрования. Если задача проста и не требует особой криптко-стойкости, то зачем нагружать программу лишним кодом, ведь чем он проще, тем стабильней работает софт. По этой причине, сначала рассмотрим обычное шифрование AES без тегов аутентификации МАС (в числе которых режимы CBC и CFB), а потом и AEAD-режимы CCM/GCM с дополнительными данными AAD.

Всё-что нужно режимам без МАС, это ключ шифрования и вектор инициализации IV. Как-правило, оба эти значения на подготовительном этапе генерируются рандомно, при этом мы можем выбрать один из трёх размеров ключей на своё усмотрение: 16, 24, 32 байта (для AES-128, 192, 256 соответственно). Поскольку в данных режимах нет внутреннего 4-байтного счётчика, то вектор IV должен быть строго размером 16 байт, чтобы он захватил весь первый блок AES:




Программная реализация шифрования включает в себя пять этапов по такой схеме:

1. BCryptOpenAlgorithmProvider() с аргументом "AES_ALGORITHM" – возвращает дескриптор алгоритма;
2. BCryptSetProperty() с аргументом "CHAIN_MODE_CBC" – устанавливает в свойствах дескриптора режим CBC;
3. BCryptGenRandom() – сгенерировать случайные ключ и вектор IV;
4. BCryptGenerateSymmetricKey() – получить дескриптор симметричного ключа шифрования;
5. BCryptEncrypt() – зашифровать данные ключом и вектором IV.

На что здесь следует обратить особое внимание, так это на значение вектора IV. Дело в том, что согласно докам MSDN, функция шифрования BCryptEncrypt() может испортить его при переходе к следующему блоку AES, и если мы не хотим потерять исходные данные, то должны заблаговременно сделать резервную копию оригинального IV, чтобы в первоначальном виде передать его функции последующей расшифровки BCryptDecrypt(). Иначе мы получим невалидный вектор, и данные уйдут к праотцам. Отметьте сей факт красным фломастером!

Функция-же непосредственно шифрования BCryptEncrypt() принимает на грудь 10 параметров, из них в приоритете четвёртый "PaddingInfo" и последний "dwFlags". Два этих аргумента содержат информацию о заполнении 16-байтных блоков AES, и для режимов без аутентификации первый должен быть нуль, а во-втором нужно выставить флаг авто-выравнивания "BCRYPT_BLOCK_PADDING". Если его не указать, функция шифрования начнёт требовать от нас, чтобы мы подавали на вход кратные блоку AES данные, т.е. минимум 16 и дальше 32,48 и т.д. байт. Так-что паддинг ставим в единицу, что даст возможность вводить пароли произвольной длины.


C-подобный:


Код:
NTSTATUS BCryptEncrypt
;
// 
cinvoke  printf
,

;
// Получить дескриптор AES-256/CBC, и собрать об алгоритме инфу.
;
// Для этого в свойствах дескриптора нужно выставить флаг "BCRYPT_CHAIN_MODE_CBC"
invoke  BCryptOpenAlgorithmProvider
,
aesAlgHndl
,
BCRYPT_AES_ALGORITHM
,
0
,
0
invoke  BCryptSetProperty
,
[
aesAlgHndl
]
,
BCRYPT_CHAINING_MODE
,
BCRYPT_CHAIN_MODE_CBC
,
30
,
0
invoke  BCryptGetProperty
,
[
aesAlgHndl
]
,
BCRYPT_BLOCK_LENGTH
,
aesBlockLen
,
4
,
pcbResult
,
0
invoke  BCryptGetProperty
,
[
aesAlgHndl
]
,
BCRYPT_OBJECT_LENGTH
,
aesObjLen
,
4
,
pcbResult
,
0
mov     eax
,
[
aesBlockLen
]
mov     ebx
,
eax
         shl     ebx
,
3
cinvoke  printf
,

,
eax
,
ebx
,
[
aesObjLen
]
;
// Сгенерим 32-байт рандом для ключа
invoke  BCryptGenRandom
,
0
,
keyRandom
,
32
,
BCRYPT_USE_SYSTEM_PREFERRED_RNG
        cinvoke  printf
,

mov     ecx
,
32
mov     esi
,
keyRandom
         call    PrintHexString
;
// Сгенерим 16-байт рандом для вектора IV
invoke  BCryptGenRandom
,
0
,
ivRandom
,
16
,
BCRYPT_USE_SYSTEM_PREFERRED_RNG
        cinvoke  printf
,

mov     ecx
,
16
mov     esi
,
ivRandom
         push    esi ecx
         call    PrintHexString
;
// Функция Encrypt() портит оригин.IV в буфере, поэтому сделаем его бэкап для расшифровки
mov     edi
,
ivBackup
         pop     ecx esi
         rep     movsb
;
//************* ШИФРОВАНИЕ **********************************
;
// Запрашиваем пасс юзера в свой буфер
cinvoke  printf
,

cinvoke  scanf
,

,
buff
         invoke  lstrlen
,
buff
         mov
[
passLen
]
,
eax
;
// Получим дескриптор ключа AES-256/CBC
invoke  BCryptGenerateSymmetricKey
,
[
aesAlgHndl
]
,
aesKeyHndl
,
0
,
0
,
keyRandom
,
32
,
0
;
// Первый вызов, чтобы узнать размер выходных данных (в переменную "pcbResult")
invoke  BCryptEncrypt
,
[
aesKeyHndl
]
,
buff
,
[
passLen
]
,
0
,
ivRandom
,
16
,
\
0
,
0
,
pcbResult
,
BCRYPT_BLOCK_PADDING
;
// Второй вызов - непосредственно шифрование в буфер "encryptBuff"
invoke  BCryptEncrypt
,
[
aesKeyHndl
]
,
buff
,
[
passLen
]
,
0
,
ivRandom
,
16
,
\
                               encryptBuff
,
[
pcbResult
]
,
pcbResult
,
BCRYPT_BLOCK_PADDING

        cinvoke  printf
,

,
[
passLen
]
,
[
pcbResult
]
,
encryptBuff
;
//************* РАСШИФРОВКА **********************************
;
// Соответственно чтобы обратно расшифровать пароль,
;
// нужно вызвать BCryptDecrypt() передав ей: ключ, оригинальный IV, и адрес данных
cinvoke  printf
,

;
// Расшифровка - получим дескриптор ключа из значения
invoke  BCryptGenerateSymmetricKey
,
[
aesAlgHndl
]
,
aesKeyHndl
,
0
,
0
,
keyRandom
,
32
,
0
;
// Запросим в EAX размер зашифрованных данных
invoke  lstrlen
,
encryptBuff
         push    eax
;
// Первый вызов, чтобы узнать размер выходных данных (в переменную "pcbResult")
invoke  BCryptDecrypt
,
[
aesKeyHndl
]
,
encryptBuff
,
eax
,
0
,
ivBackup
,
16
,
\
0
,
0
,
pcbResult
,
BCRYPT_BLOCK_PADDING
;
// Второй вызов - непосредственно расшифровка в буфер "decryptBuff"
pop     eax
         invoke  BCryptDecrypt
,
[
aesKeyHndl
]
,
encryptBuff
,
eax
,
0
,
ivBackup
,
16
,
\
                               decryptBuff
,
[
pcbResult
]
,
pcbResult
,
BCRYPT_BLOCK_PADDING

        cinvoke  printf
,

,
[
pcbResult
]
,
decryptBuff

@exit
:
cinvoke  _getch
        cinvoke  exit
,
0
;
//------------
;
//----- Процедура вывода Hex-строки на консоль --------------
;
//----- на входе: ESI = указатель на данные, ECX = длина
proc PrintHexString
@@
:
xor     eax
,
eax
          lodsb
          push    ecx esi
         cinvoke  printf
,

,
eax
          pop     esi ecx
          loop    @b
          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'


Как видим тут всё просто, но программисты предпочитают что-то позабористей, ведь за почти такое-же количество "приседаний" можно получить более лучший эффект. Достаточно в этом коде задействовать параметр "pPaddingInfo", как в нашем распоряжении оказываются дополнительные данные AAD, с возможностью получить тег-аутентификации МАС.

Если мы сталкиваемся с зашифрованными данными и их длина кратна 16-ти, то можно предположить, что это AES в блочном режиме. Если-же длина кратна восьми, как вариант перед нами DES.

4.1. Практика – шифрование AES-256 в режиме GCM

Режимы шифрования с аутентификацией GCM/CCM можно сравнить с безоборотным метанием ножа – они при любых обстоятельствах бьют точно в цель. Такие бонусы мы получаем благодаря структуре "BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO", указатель на которую передаётся в параметре(4) Padding.

C этой структурой связана одна мерзкая неприятность – по неизвестным причинам, её размер везде (на MSDN и хидере bcrypt.h) указывается на 8-байт меньше, чем этого требуют функции BCryptEncrypt/Decrypt(). Правится баг обычным добавление в хвост структуры ещё одного 8-байтного поля, которое я обозначил как резерв. Иначе крипто-функции выше возвращают ошибку
Код:
"INVALID_PARAMETR"
с кодом
Код:
0xC000000d
. Вот её исправленное описание:


C-подобный:


Код:
struct BCRYPT_AUTH_CIPHER_MODE_INFO
   cbSize          dd
64
;
// размер данной структуры (должен быть мин.64 байт для х32)
dwInfoVer       dd
1
;
// версия структуры, всегда = 1
pbNonce         dd
0
;
// линк на буфер, содержащий IV (dq для х64)
cbNonce         dd
0
;
// размер IV = 12 байт
pbAuthData      dd
0
;
// линк на буфер с данными-аутентификации AAD (dq для х64)
cbAuthData      dd
0
;
//
pbTag           dd
0
;
// линк на буфер для кода-аутентификации (dq для х64)
cbTag           dd
0
;
//
pbMacContext    dd
0
;
// линк на буфер для МАС, (только при связанном вызове, dq для х64)
cbMacContext    dd
0
;
//
cbAAD           dd
0
;
// AAD (только при связанном вызове)
cbData          dq
0
;
// длина данных полезной нагрузки (только при связанном вызове)
dwFlags         dd
0
;
// флаг связывания вызовов = 0
Reserved        dq
0
;
// 
cinvoke  printf
,

;
// Получить дескриптор AES-256/GCM, и собрать о нём инфу.
;
// В свойствах дескриптора ставим флаг "BCRYPT_CHAIN_MODE_GCM"
invoke  BCryptOpenAlgorithmProvider
,
aesAlgHndl
,
BCRYPT_AES_ALGORITHM
,
0
,
0
invoke  BCryptSetProperty
,
[
aesAlgHndl
]
,
BCRYPT_CHAINING_MODE
,
BCRYPT_CHAIN_MODE_GCM
,
30
,
0
invoke  BCryptGetProperty
,
[
aesAlgHndl
]
,
BCRYPT_BLOCK_LENGTH
,
aesBlockLen
,
4
,
pcbResult
,
0
invoke  BCryptGetProperty
,
[
aesAlgHndl
]
,
BCRYPT_OBJECT_LENGTH
,
aesObjLen
,
4
,
pcbResult
,
0
invoke  BCryptGetProperty
,
[
aesAlgHndl
]
,
BCRYPT_AUTH_TAG_LENGTH
,
tagLen
,
4
*
3
,
pcbResult
,
0
invoke  BCryptGetProperty
,
[
aesAlgHndl
]
,
BCRYPT_CHAINING_MODE
,
chainMode
,
64
,
pcbResult
,
0
;
//
,
\
                         chainMode
,
eax
,
ebx
,
[
aesObjLen
]
,
\
[
tagLen
.
dwMinLen
]
,
[
tagLen
.
dwMaxLen
]
,
[
tagLen
.
dwIncrement
]
;
// Сгенерим рандомы Key(32) + IV(12) байт, и сохраним IV в резервный бэкап
cinvoke  printf
,

invoke  BCryptGenRandom
,
0
,
ivRandom
,
12
,
BCRYPT_USE_SYSTEM_PREFERRED_RNG
         mov     ecx
,
12
mov     esi
,
ivRandom
         push    ecx esi
         call    PrintHexString

         pop     esi ecx
         mov     edi
,
ivBackup
         rep     movsb

        cinvoke  printf
,

invoke  BCryptGenRandom
,
0
,
keyRandom
,
32
,
BCRYPT_USE_SYSTEM_PREFERRED_RNG
         mov     ecx
,
32
mov     esi
,
keyRandom
         call    PrintHexString
;
//***********************************************
;
// Запрашиваем в свои буферы пароль и "AAD"
cinvoke  printf
,

cinvoke  gets
,
buff
         invoke  lstrlen
,
buff
         mov
[
passLen
]
,
eax

        cinvoke  printf
,

cinvoke  gets
,
authData
         invoke  lstrlen
,
authData
         mov
[
authLen
]
,
eax
;
//************* ШИФРОВАНИЕ **********************
;
// Запросить дескриптор ключа AES-256/GCM
invoke  BCryptGenerateSymmetricKey
,
[
aesAlgHndl
]
,
aesKeyHndl
,
0
,
0
,
keyRandom
,
32
,
0
;
// Заполнить структуру "BCRYPT_AUTH_CIPHER_MODE_INFO"
mov
[
authInfo
.
pbNonce
]
,
ivRandom
         mov
[
authInfo
.
cbNonce
]
,
12
mov
[
authInfo
.
pbTag
]
,
tagBuff
         mov
[
authInfo
.
cbTag
]
,
16
mov     eax
,
[
authLen
]
mov
[
authInfo
.
pbAuthData
]
,
authData
         mov
[
authInfo
.
cbAuthData
]
,
eax

         invoke  BCryptEncrypt
,
[
aesKeyHndl
]
,
buff
,
[
passLen
]
,
authInfo
,
0
,
0
,
\
                               encryptBuff
,
[
passLen
]
,
pcbResult
,
0
cinvoke  printf
,

mov     ecx
,
12
mov     esi
,
ivBackup
         call    PrintHexString

        cinvoke  printf
,

,
encryptBuff

        cinvoke  printf
,

mov     ecx
,
16
mov     esi
,
tagBuff
         call    PrintHexString
;
//************* РАСШИФРОВКА **********************
;
// Очистить введённые данные в буфере AAD
mov     edi
,
authData
         mov     ecx
,
128
/
4
xor     eax
,
eax
         rep     stosd
;
// Запрос юзеру на подтверждение AAD
cinvoke  printf
,

cinvoke  gets
,
authData
         invoke  lstrlen
,
authData
         mov
[
authLen
]
,
eax
;
// Узнать длину зашифрованного пароля
invoke  lstrlen
,
encryptBuff
         mov
[
passLen
]
,
eax
;
// Заполнить структуру "BCRYPT_AUTH_CIPHER_MODE_INFO"
mov
[
authInfo
.
pbNonce
]
,
ivBackup
         mov
[
authInfo
.
cbNonce
]
,
12
mov
[
authInfo
.
pbTag
]
,
tagBuff
         mov
[
authInfo
.
cbTag
]
,
16
mov     eax
,
[
authLen
]
mov
[
authInfo
.
pbAuthData
]
,
authData
         mov
[
authInfo
.
cbAuthData
]
,
eax
;
// Запросить дескриптор ключа AES-256/GCM
invoke  BCryptGenerateSymmetricKey
,
[
aesAlgHndl
]
,
aesKeyHndl
,
0
,
0
,
keyRandom
,
32
,
0
;
// Расшифровать пароль!
invoke  BCryptDecrypt
,
[
aesKeyHndl
]
,
encryptBuff
,
[
passLen
]
,
authInfo
,
0
,
0
,
\
                               decryptBuff
,
[
passLen
]
,
pcbResult
,
0
or      eax
,
eax
         je      @f
        cinvoke  printf
,

jmp     @exit
@@
:
cinvoke  printf
,

,
decryptBuff

@exit
:
cinvoke  _getch
        cinvoke  exit
,
0
;
//------------
;
//----- Процедура вывода Hex-строки на консоль ---------
;
//----- на входе: ESI = указатель на данные, ECX = длина
proc PrintHexString
@@
:
xor     eax
,
eax
          lodsb
          push    ecx esi
         cinvoke  printf
,

,
eax
          pop     esi ecx
          loop    @b
          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'


Если расчехлить отладчик и вскормить ему этот код, то нырнув в функцию BCryptEncrypt() по F7 можно обнаружить, что функция не использует преимущества аппаратного шифрования AES-NI, а весь алгоритм реализует исключительно обычными
Код:
MOV
,
Код:
XOR
и прочими. То есть почему-то Intel ввела инструкции AES-NI, а мелкософт не использует их. Может на это есть свои причины?

Обратите внимание на формат результата шифрования – он включает в себя три составляющие: 12-байтный вектор IV, зашифрованный пароль, и в хвосте тег-аутентификации GМАС. А вот данные AAD как-правило не передаются принимающей стороне, иначе в них теряется смысл. Их нужно держать в тайне, накрыв протектом на уровне самой программы. Это-же относится и 32-байтному ключу шифрования AES. Рассмотрим, как справляется с этой задачей, например, браузер Хром.

5. Использование AEAD в браузерах

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

На предыдущем скрине, я не зря расположил результат шифрования AES/GCM в таком порядке. По сути, от перестановки мест слагаемых сумма не меняется. Однако интернет браузеры Chrome версии 80+ держат эту информацию в своих базах SQLite именно в таком виде, только добавляют к ним ещё и сигнатуру "v10". То-есть получаем сл.табличку:




Это типичный формат хранения хромом паролей и кукисов, базы которых можно найти по следующим адресам:

• Пароли – AppData\Local\Google\Chrome\User Data\Default\Login Data
• Кукисы – AppData\Local\Google\Chrome\User Data\Default\Cookies

Открываем базу кукисов в каком-нибудь редакторе SQLite3 (типа SQLite Expert Personal), и обнаруживаем в ней две таблицы с названиями "cookies" и "meta". В первой имеется столбец "encrypted_value", где и хранятся зашифрованные алгоритмом AES-256/GCM непосредственно куки различных сайтов. Если щёлкнуть на пимпу любой строки в этом столбце, то перед нами откроется встроенный Hex-редактор, где будет красоваться соответствующий закриптованный кукис. Вот как это выглядит у меня:



В процессе расшифровки данного кукиса, нам нужно просто отбросить сигнатуру "v10" и передать всю оставшуюся информацию в свой код. Отметим, что Хром не использует данные AAD, тогда для достижении цели нам остаётся добыть только 32-байтный ключ шифрования. Как и следовало ожидать, он прописан там-же, только в файле Json по адресу:

• AppData\Local\Google\Chrome\User Data\Local State

Помимо ключа, в этом файле хранится всякое барахло,

а сам ключ в нём можно найти по Json-строке
Код:
"os_crypt":{"encrypted_key":
За этой строкой и вплоть до обратной/фигурной скобки, будет лежать закодированный в Base64 ключ. Значит нам нужно сначала раскодировать его из Base64, а затем снять с него протект при помощи функции DPAPI CryptUnprotectData() (рассматривалась вэтой статье). После всех этих операций мы получим ключ расшифровки кукисов алгоритмом AES-256/GCM, а так-как имеем уже IV, MAC и зашифрованные данные, то дело остаётся за малым.



6. Послесловие

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

У нас не было-бы безопасного современного Интернета без работы В.Реймена, Д.Дэемен, Д.Виеги, Д.МакГрю и бесчисленного множества других криптографов и исследователей безопасности, которые сделали возможным использование AES в режиме GCM. Это по истине один из самых продвинутых на сегодняшний день алгоритмов шифрования, который прочно пустил свои корни во-все направления современной сферы IT.

В скрепке лежат три рассмотренных выше исполняемых файла, а так-же инклуд "вcrypt.inc" для сборки исходников.

Надеюсь ещё увидимся, удачи, пока!
 
Ответить с цитированием

  #2  
Старый 08.07.2021, 12:28
rusrst
Новичок
Регистрация: 18.04.2021
Сообщений: 0
С нами: 2670034

Репутация: 0
По умолчанию

Прекрасные статьи! Было бы ещё очень интересно в Вашем исполнении прочитать про winsocs и wininet.
 
Ответить с цитированием

  #3  
Старый 08.07.2021, 12:36
Mikl___
Новичок
Регистрация: 14.12.2019
Сообщений: 0
С нами: 3377258

Репутация: 0
По умолчанию

 
Ответить с цитированием

  #4  
Старый 08.07.2021, 12:44
rusrst
Новичок
Регистрация: 18.04.2021
Сообщений: 0
С нами: 2670034

Репутация: 0
По умолчанию

Я читал про winsocs, и даже работал с ними но у автора уж больно хорошо и красиво выходит. Ну есть ещё wininet, которая организует протоколы высокого уровня над winsocs и которой уделено уже гораздо меньше внимания
За ссылки спасибо, ознакомлюсь!
P.s. ещё раз спасибо за ссылки, они как-то мимо меня прошли!!!
 
Ответить с цитированием

  #5  
Старый 15.03.2023, 00:32
rusrst
Новичок
Регистрация: 18.04.2021
Сообщений: 0
С нами: 2670034

Репутация: 0
По умолчанию

Вы так давно не писали статьи, жаль...
 
Ответить с цитированием
Ответ





Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.