• XSS.stack #1 – первый литературный журнал от юзеров форума

Помогите разобраться с криптом через WinAPI (CNG)

omar_hayat

CD-диск
Пользователь
Регистрация
26.08.2024
Сообщения
15
Реакции
14
Всем привет!

Пытаю WinAPI`шную технологию шифрования данных через CNG с симметричным алгоритмом AES-GCM. Прочёл кучу теории. Просмотрел чуть меньше исходников под реализацию именно AES-GCM. Решил реализовать в таком виде исходный код
C++:
#include <windows.h>
#include <bcrypt.h>
#include <iostream>
#include <vector>
#include <string>

#pragma comment(lib, "bcrypt.lib")

void handleError(NTSTATUS status, const wchar_t* s) {
    if (status != STATUS_SUCCESS) {
        std::cerr << "Error: " << std::hex << status << std::endl;
        std::wcout << s << std::endl;
        exit(1);
    }
}

std::vector<BYTE> encrypt(const std::string& plaintext, const std::vector<BYTE>& key, const std::vector<BYTE>& iv) {
    BCRYPT_ALG_HANDLE hAlg;
    BCRYPT_KEY_HANDLE hKey;
    DWORD cbData;
    NTSTATUS status;

    // Создаем алгоритм AES
    handleError(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0), L"Create AES");
    
    // Создаем ключ
    handleError(BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, (PUCHAR)key.data(), key.size(), 0), L"Create key");

    // Устанавливаем параметры для GCM
    handleError(BCryptSetProperty(hKey, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0), L"Create GCM");
    handleError(BCryptSetProperty(hKey, BCRYPT_INITIALIZATION_VECTOR, (PUCHAR)iv.data(), iv.size(), 0), L"Create IV");

    // Выделяем память для шифрованного текста
    std::vector<BYTE> ciphertext(plaintext.size());
    DWORD cbCiphertext = 0;

    // Шифруем данные
    handleError(BCryptEncrypt(hKey, (PUCHAR)plaintext.data(), plaintext.size(), NULL, (PUCHAR)iv.data(), iv.size(),
                              ciphertext.data(), ciphertext.size(), &cbCiphertext, 0), L"Crypt");

    // Очищаем ресурсы
    BCryptDestroyKey(hKey);
    BCryptCloseAlgorithmProvider(hAlg, 0);

    ciphertext.resize(cbCiphertext);
    return ciphertext;
}

int main() {
    // Пример данных
    std::string plaintext = "Hello, World!";
    
    // Ключ 16 байт для AES-128
    std::vector<BYTE> key = { 
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
    };
    
    //IV 12 байт (4 байта будет заполняться счётчиком
    std::vector<BYTE> iv = { 
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b
    };

    // Шифруем
    std::vector<BYTE> ciphertext = encrypt(plaintext, key, iv);

    // Выводим шифрованные данные в шестнадцатичном формате
    for (BYTE byte : ciphertext) {
        std::cout << std::hex << (int)byte;
    }
    std::cout << std::endl;

    return 0;
}

Код отваливается на строке:
C++:
handleError(BCryptSetProperty(hKey, BCRYPT_INITIALIZATION_VECTOR, (PUCHAR)iv.data(), iv.size(), 0), L"Create IV");
с ошибкой функции BCryptSetProperty 0xC000000D. Вроде этот код дешифруется так: STATUS_INVALID_PARAMETER.

Через пошаговую загрузку посмотрел какие значение всех параметров вводятся в функцию. Всё норм.
Вывел эти же значения через cout на экран. Так же всё на месте.

Решил поэкспериметировать со значениями iv. Увеличил до 16 байт. Тогда отвал переместился на строку
C++:
 handleError(BCryptEncrypt(hKey, (PUCHAR)plaintext.data(), plaintext.size(), NULL, (PUCHAR)iv.data(), iv.size(),ciphertext.data(), ciphertext.size(), &cbCiphertext, 0), L"Crypt");
с тем же кодом ошибки.

Чтение документации с сайта мелкомягких даёт мало инфы. Вообще странно, что ошибка не указывает какой именно из параметров косячный.
Гугление особо не даёт инфы как эту проблему решать. Находил инфу по другим ЯП. Там помогало явное задание dwFlags=0. У меня он принимает это значение в качестве аргумента обоих функций.

Не знаю ещё в какую сторону капнут. Поэтому прошу помощи. Кто то знает как завести эту мелкомягкую балалайку?
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Я не помню конкретно детали алгоритма со счетчиком Галуа, но разве там вектор инициализации? Там должен быть тэг и нонсе (BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO). Погугли конкретно как пользоваться AES в режиме GCM через BCrypt, и как правильно туда что установить в свойствах. Но, вообще говоря, не знаю зачем тебе нужен GCM может быть, если ты не пытаешься из Хрома пароли расшифровать, обычного CBC тебе должно хватить.
 
но разве там вектор инициализации? Там должен быть тэг и нонсе
IV и nonce отличаются тем что вектор задаёшь размером 16 байт, а nonce 12. Остальные 4 байта идут под счётчик. Некоторые авторы говорят что разници типа нет поэтому:) Я нашёл пару примеров кода на других ЯП. По сути, если использую WinAPI не важно какой язык изпользуешь. Главное структуры правельно заполнить и вызовы сделать. Видимо у меня это не вышло. Попробую с этой структурой (BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO) ещё поразбираться.
Но, вообще говоря, не знаю зачем тебе нужен GCM может быть, если ты не пытаешься из Хрома пароли расшифровать, обычного CBC тебе должно хватить.
Тоже уже начал над этим подумывать. Если трафик заворачиваю в HTTPS он уже отвечает за целостность данных при доставке. И нужда в дополнительной проверке отпадает. Только тяжело оставлять дело неоконченным. Хочется всё таки заставить код работать.
 
Видимо у меня это не вышло. Попробую с этой структурой (BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO) ещё поразбираться
Размер этой структуры должен быть обязательно кратен 64-байт, а в сишном хидере он на 8-байт меньше. Из-за этого возвращается ошибка "STATUS_INVALID_PARAMETER". Посмотри в отладчике значение первого поля "cbSize" этой структуры. В общем у этого CNG основные праблы с выравниванием структур в памяти.
 
Размер этой структуры должен быть обязательно кратен 64-байт, а в сишном хидере он на 8-байт меньше. Из-за этого возвращается ошибка "STATUS_INVALID_PARAMETER". Посмотри в отладчике значение первого поля "cbSize" этой структуры. В общем у этого CNG основные праблы с выравниванием структур в памяти.
Находил тоже такую инфу. Поначалу я применял макрос BCRYPT_INIT_AUTH_MODE_INFO для заполнения этой структуры. В отладчике поле cbSize вообще было 88 байт. Потом я руками устанавливал значение 64 ошибка не менялась.

Сейчас код заработал. Очень помогла ссылка на стоковерфлоу, которую дал bergamot . Ошибка была передаче значения в поле Tag. Кстати, работает и на 88 байтах, которые заполнились через макрос BCRYPT_INIT_AUTH_MODE_INFO
 
Never call BCryptSetProperty with BCRYPT_INITIALIZATION_VECTOR in GCM mode. Instead, always fill the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO sturct (especially pbNonce/cbNonce + pbTag/cbTag)

PS. Never mind.
 
Последнее редактирование:


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх