Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Система построения проектов CMake. Учебник.pdf
X
- •Введение
- •Модульное программирование
- •Автоматизация построения проектов
- •Обзор инструментов построения проектов
- •qmake
- •Генераторы
- •Входные файлы
- •Пути
- •Синтаксис
- •Команды
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Основные концепции
- •Строки
- •Свойства
- •Примеры простых проектов
- •Команды общего назначения
- •cmake_minimum_required()
- •project()
- •include()
- •message()
- •Команды описания целей
- •add_executable()
- •add_library()
- •add_subdirectory()
- •Команды настроек целей
- •include_directories()
- •target_include_directories()
- •add_definitions(), add_compile_options()
- •target_compile_definitions()
- •target_compile_options()
- •target_compile_features()
- •target_link_libraries()
- •add_dependencies()
- •Команды обработки данных
- •math()
- •list()
- •Команды управляющих конструкций
- •foreach(), endforeach()
- •Команды работы с файлами
- •get_filename_component()
- •Команды добавления специальных целей
- •configure_file()
- •add_test(), enable_testing()
- •install()
- •add_custom_target()
- •Прочие команды
- •find_package()
- •get_property(), set_property()
- •Виды конфигураций
- •Выражения генераторов
- •Информационные выражения
- •Логические выражения
- •Преобразующие выражения
- •Вспомогательные выражения
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Интерфейс подключения библиотек
- •Подключение заголовочной библиотеки
- •Частичная подмена стандартной библиотеки
- •Интерфейс подключения библиотек
- •Локализация приложения
- •Установка приложения
- •Инструменты разработки
- •Управление версиями
- •Генерирование документации
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Заключение
- •Библиография
- •Ответы на тесты

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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
