Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Система построения проектов CMake. Учебник.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
3.4. Crypto++
ПРИМЕР Пусть требуется зашифровать тестовое сообщение, после чего результат расшифровать и вывести на печать. В качестве ме­тода шифрования необходимо использовать блочный алгоритм AES в режиме счётчика со случайными вектором инициализа­ции и ключом размера 128 бит. Реализация построения проекта должна удовлетворять перечисленным выше требованиям. Ре­шением этой задачи является код, расположенный в следующих файлах.
Файл ex-cryptopp.cpp:
#include "aes.h"
#include "ccm.h"
#include "osrng.h"
#include "filters.h"
#include "secblock.h"
#include <iostream>
#include <string>
typedef CryptoPP::CTR_Mode <CryptoPP::AES> AESWithCTR;
const char g_acszPlainText[] =
"0123456789ABCDEF Hello World! 0123456789ABCDEF";
template <class TCryptor>
std::string crypt(
const CryptoPP::SecByteBlock &rcKey,
351
3. Примеры использования пакетов
const CryptoPP::SecByteBlock &rcIV,
const std::string &rcInput)
{
TCryptor cryptor;
cryptor.SetKeyWithIV(
rcKey.data(), rcKey.size(), rcIV.data());
std::string s_result;
CryptoPP::StringSource source(
rcInput, true,
new CryptoPP::StreamTransformationFilter(
cryptor, new CryptoPP::StringSink(s_result)));
//
return s_result;
}
int main()
{
CryptoPP::SecByteBlock key(
CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::SecByteBlock iv(
CryptoPP::AES::BLOCKSIZE);
CryptoPP::AutoSeededRandomPool generator;
generator.GenerateBlock(key, key.size());
generator.GenerateBlock(iv, iv.size());
//
std::string s_encrypted =
crypt <AESWithCTR::Encryption> (
352
3.4. Crypto++
key, iv, g_acszPlainText);
std::string s_decrypted =
crypt <AESWithCTR::Decryption> (
key, iv, s_encrypted);
//
std::cout << s_decrypted << std::endl;
}
Здесь в первой строке после подключения заголовочных
файлов выполняется конкретизация библиотечного шабло­на CTR_Mode <>, реализующего схему со счётчиком, классом, реализующим интерфейс симметричного шифра (класс AES), который используется схемой в процессе шифрования блоков. Внутри шаблона CTR_Mode <> определены типы Encryption и Decryption, реализующие интерфейс SymmetricCipher и предназначенные соответственно для шифрования и дешиф­рования. Эти классы используются в дальнейшем в функции
main().
Дальше приводится объявление константной строки
g_acszPlainText, которая представляет собой исходное со­общение. Длина строки выбрана таким образом, что она, с одной стороны, больше длины блока алгоритма AES (16 байт), а с другой — не кратна ему. Это необходимо для проверки корректности работы схемы CTR_Mode <>.
Следующим идёт определение шаблонной функции
crypt <> (), которая предназначена одновременно для шиф- рования и дешифрования — в зависимости от передаваемого
353
3. Примеры использования пакетов
ей шаблонного аргумента (типы Encryption или Decryption, о которых шла речь выше). Параметрами функции явля­ются ключ, вектор инициализации и сообщение, которое требуется обработать. Первые два параметра имеют тип
«const SecByteBlock &», который отвечает за автоматическое
обнуление памяти после её использования в целях безопас­ности. В реальных приложениях исходное сообщение нужно передавать таким же способом.
В теле функции crypt <> () объявляется переменная ти­па Encryption или Decryption, которой далее передаются ключ и вектор инициализации. Для ключа также передаётся его раз­мер, так как один и тот же блочный шифр может работать с клю­чами нескольких размеров (как в случае с AES).
Далее в функции crypt <> () объявляется переменная ти­па StringSource, задача которой состоит в передаче данных из источника на вход связанного с ней алгоритма преобразо­вания. Класс StringSource является реализацией абстрактно­го класса Source (источник данных в последовательности пре­образований), которая отвечает за чтение данных из строки
(std::string) или массива символов. Другие реализации мо-
гут считывать данные из файла, по сети и т. д. Класс Source, в свою очередь, является реализацией абстрактного класса Filter, производного от интерфейса BufferedTransformation. Последний обобщает понятие преобразователя потока дан­ных, которые считываются, возможно, порциями, и результат преобразования которых накапливается во внутреннем буфе­ре. Конструктору переменной типа StringSource передают-
354
3.4. Crypto++
ся строка-источник (сообщение для шифрования или расшиф­рования), параметр pumpAll (true), указывающий на то, что в конструкторе необходимо считать столько данных из ис­точника, сколько возможно, а также прикрепляемый объ­ект преобразования следующего этапа (тоже производный от BufferedTransformation). В данном случае это объект ти­па StreamTransformationFilter, который является адаптером производных классов от StreamTransformation, реализуя ин­терфейс Filter. Конструктору объекта передаются адаптируе­мый объект (переменная cryptor) и прикрепляемый объект преобразования следующего этапа. В качестве него использует­ся объект типа StringSink, который является реализацией аб­страктного класса Sink (последний этап цепочки преобразова­ний, «сток»), также производного от BufferedTransformation. Класс StringSink отвечает за добавление приходящих ему на вход данных в конец заданной строки. В рассматрива­емом примере эта строка в итоге возвращается функцией
crypt <> ().
Следует отметить, что в приведённом здесь исходном коде объекты типов StreamTransformationFilter и StringSink со­здаются в динамической памяти внутри приложения, а удаля­ются владеющими ими объектами внутри библиотеки Crypto++. Это является примером описанной выше проблемы использо­вания разных куч в приложении и динамической библиотеке Crypto++, собранной при помощи Visual C++.
Работа функции main() заключается в заполнении случай­ными данными ключа и вектора инициализации, вызова функ-
355
3. Примеры использования пакетов
ции crypt <> (), конкретизированной алгоритмом шифрова­ния, с исходным сообщением, затем вызовом её же, конкретизи­рованной алгоритмом расшифрования, с результатом её преды­дущего вызова, и выводе на печать результата последнего вызо­ва.
Файл cryptopp-dll.cpp, предназначенный для исправле­ния проблемы различных куч в приложении и динамической библиотеке Crypto++, собранной при помощи Visual C++:
#ifdef _MSC_VER
#include <new>
typedef void * (*PNew)(size_t);
typedef void (*PDelete)(void *);
typedef std::new_handler (*PSetNewHandler)(
std::new_handler);
static PNew g_pNew = nullptr;
static PDelete g_pDelete = nullptr;
extern "C" __declspec (dllexport)
void SetNewAndDeleteFromCryptoPP(
PNew pNew, PDelete pDelete,
PSetNewHandler pSetNewHandler)
{
g_pNew = pNew;
g_pDelete = pDelete;
356
}
void *operator new (size_t size)
{
return g_pNew(size);
}
void operator delete (void *p)
{
g_pDelete(p);
}
3.4. Crypto++
#endif // _MSC_VER
Здесь в начале и конце файла находится пара дирек­тив условного включения содержимого, если определён мак­рос _MSC_VER, предопределённый в компиляторе Visual C++. В файле определяются типы указателей на функции, совмести­мые со стандартными operator new (), operator delete () и std::set_new_handler(). Эти объявления скопированы из за­головочного файла dll.h библиотеки Crypto++. Сам файл не под­ключается из кода примера, так как он содержит директи­вы компилятора Visual C
++
для связывания с библиотекой cryptopp.dll, а эта задача возлагается на систему CMake (ко­торая связывает приложение с нужной версией библиотеки по сложным правилам).
357
3. Примеры использования пакетов
Далее определена экспортируемая исполняемым модулем функция SetNewAndDeleteFromCryptoPP(), которая будет вы­звана библиотекой cryptopp.dll при первой попытке обраще­ния к динамической памяти. Функция сохраняет в глобальных переменных передаваемые ей указатели на operator new () и operator delete () из библиотеки. Переопределяемые даль­ше функции operator new () и operator delete () для при­ложения вызывают свои аналоги из библиотеки по ранее сохра­нённым указателям. Объявление extern "C" в заголовке функ­ции предотвращает добавление информации о типах её пара­метров к её имени в таблице экспорта модуля (что делается для функций в языке C++, где разрешена перегрузка).
Файл cryptopp.cmake:
set(OUT_SUBDIR Output)
set(LIB_NAMES cryptopp)
set(ADD_DLL_IMPORT FALSE)
set(ADD_DLL_MINGW_IMPORT FALSE)
if(MSVC)
set(LIB_NAMES cryptlib)
#
if(BUILD_SHARED_LIBS)
set(OUT_SUBDIR DLL_Output)
358
list(APPEND LIB_NAMES cryptopp)
set(ADD_DLL_IMPORT TRUE)
#
add_library(CryptLib STATIC IMPORTED)
endif()
elseif(MINGW OR CYGWIN)
if(BUILD_SHARED_LIBS)
set(LIB_NAMES libcryptopp.dll.a)
set(ADD_DLL_MINGW_IMPORT TRUE)
else()
set(LIB_NAMES libcryptopp.a)
endif()
elseif(UNIX)
if(BUILD_SHARED_LIBS)
set(LIB_NAMES libcryptopp.so)
3.4. Crypto++
else()
set(LIB_NAMES libcryptopp.a)
endif()
endif()
add_library(CryptoPP STATIC IMPORTED)
if(ADD_DLL_IMPORT)
get_filename_component(
CRYPT_SRC cryptopp-dll.cpp ABSOLUTE)
set_property(
TARGET CryptoPP
APPEND
PROPERTY INTERFACE_SOURCES
"${CRYPT_SRC}")
#
359
3. Примеры использования пакетов
set_property(
TARGET CryptoPP
APPEND
PROPERTY INTERFACE_COMPILE_DEFINITIONS
CRYPTOPP_IMPORTS)
endif()
if(ADD_DLL_MINGW_IMPORT)
set_property(
TARGET CryptoPP
APPEND
PROPERTY INTERFACE_COMPILE_DEFINITIONS
CRYPTOPP_MINGW_IMPORTS)
endif()
find_path(
CRYPT_H_DIR aes.h
PATH_SUFFIXES cryptopp)
if(NOT CRYPT_H_DIR)
message(
FATAL_ERROR "Could not find aes.h")
endif()
foreach(CONFIG Debug Release)
set(NUM_LIB 0)
foreach(LIB IN LISTS LIB_NAMES)
360
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]