Crypto library с linux

Знакомство с библиотекой шифрования libgcrypt

Добрый день, хабрахабр!

В процессе написания одной из своих программ мне понадобилось разобраться с библиотекой шифрование и де-шифрования текста. Я разобрался и теперь хочу поделиться накопленным опытом и знаниями с сообществом.

В данной статье речь пойдет о библиотеке libgcrypt.

Предисловие

Программу я пишу под ОС Linux. Поэтому и библиотеку искал тоже под эту ОС. Я не пытался найти десятки библиотек, чтобы потом выбрать лучшую. Я выбрал ту, которая подходила под мои нужды — раз, которая используется в достаточно известных продуктах — два.

О самой библиотеке libgcrypt

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

Процесс шифрования

В процессе шифрования мы будем использовать следующие функции (указаны в порядке использования):

  1. gcry_cipher_open — создаем дескриптор контекста
  2. gcry_cipher_setkey — задаем пароль
  3. gcry_cipher_setiv — задаем вектор инициализации
  4. gcry_cipher_encrypt — функция шифрования текста
  5. gcry_cipher_close — закрытие дескриптора контекста

Теперь подробнее о каждой функции.

Функция создает дескриптор контекста, который необходим для дальнейших функций шифрования и возвращает дескриптор в ‘hd’. В случае ошибки возвращается ненулевой код ошибки.

hd — указатель на наш будущий дескриптор контекста.

algo — алгоритм, который мы собираемся использовать для шифрования текста. Примеры:
GCRY_CIPHER_IDEA — алгоритм IDEA. Хоть его и можно выбрать, но работать не будет. Так как алгоритм запатентованный, для него нет реализации в свободной библиотеке.
GCRY_CIPHER_3DES — (Triple-DES with 3 Keys as EDE) симметричный блочный шифр.
GCRY_CIPHER_BLOWFISH — алгоритм Blowfish. Текущая реализация позволяет использовать только 128-битный ключ.
Так же есть RIJNDAEL, TWOFISH, AES, SERPENT и так далее.
(Весь список алгоритов находить здесь )

mode — один из следующих вариантов:

  • GCRY_CIPHER_MODE_NONE — не использовать никакой модификатор. (Надо избегать использования этого ключа.)
  • GCRY_CIPHER_MODE_ECB — режим электронной кодовой книги. (Electronic Code Book)
  • GCRY_CIPHER_MODE_CFB — режим обратной связи по шифротексту. (Cipher FeedBack)
  • GCRY_CIPHER_MODE_CBC — режим сцепления блоков шифротекста. (Cipher Block Chaining)
  • GCRY_CIPHER_MODE_STREAM — режим для использования только со stream-алгоримами. К примеру, GCRY_CIPHER_MODE_STREAM.
  • GCRY_CIPHER_MODE_OFB — режим обратной связи вывода. (Output FeedBack)
  • GCRY_CIPHER_MODE_CTR — режим счетчика. (Counter)

flags — может быть 0 (нулём), либо комбинацией следующих флагов:

  • GCRY_CIPHER_SECURE — все операции расположены в защищенной памяти.
  • GCRY_CIPHER_ENABLE_SYNC — этот флаг включает режим CFB синхронизации.
  • GCRY_CIPHER_CBC_CTS — включает CTS (cipher text stealing) для режима CBC. (Флан не может быть использован одновременно с GCRY_CIPHER_CBC_MAC.) CTS режим позволяет производить изменения данных произвольной величины.
  • GCRY_CIPHER_CBC_MAC — подсчитать контрольную сумму CBC MAC keyed. (Флан не может быть использован одновременно с GCRY_CIPHER_CBC_CTS.)

Пример

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

hd — полученный ранее дескриптор
k — ключ, он же — пароль (строка символов)
l — длина ключа (strlen(k))

Большинство режимов шифрования требуют вектор инициализации, которым обычно является не секретная случайная строка, исполняющая обязанности “соли” (salt). В случае CTR режима необходимо указывать счетчик, который так же схож со значением “соли”. Чтобы установить эти значения используем функции:

  • gcry_cipher_setiv (gcry_cipher_hd_t h, const void *k, size_t l)
    Установить вектор инициализации используемый для шифрования или де-шифрования. Вектор передается как буфер k длинны l байт и копируется во внутреннюю структуру данных. Функция так же проверяет соответствует ли вектор необходимым требованиям для заданного алгоритма (algo) и режима (mode).
  • gcry_cipher_setctr (gcry_cipher_hd_t h, const void *c, size_t l)
    Установить ветор-счетчик используемый для шифрования или де-шифрования. Счетчик передается как буфер k длинны l байт и копируется во внутреннюю структуру данных. Функция так же проверяет соответствует ли вектор необходимым требованиям для заданного алгоритма (т.е. вектор должен быть такого размера как и размер блока).
Читайте также:  Активировать windows заменили материнскую плату

Вот мы и подошли к ключевому моменту — шифрование. Сам процесс шифрования выполняется функцией gcry_cipher_encrypt.

Функция может работать как с одним так и с двумя буферами. Если значение in передается как NULL и inlen равен 0 (нулю), то производится шифрование с одним буфером. Проще говоря буфер out, в котором перед вызовом функции содержит не шифрованный текст, при выходе из функции будет переписан новым текстом, шифрованным. Если значение in передается как не NULL, то inlen байт шифруется и помещается в буфер out (который должен быть не меньше размера inlen). outsize должен отображать размерность выделенного куска памяти для буфера out, чтобы функция могла проверить достаточно ли места для вывода. (Перекрытие буферов неразрешается.)

В зависимости от выбранного алгоритма и режима шифрования длина буферов должна быть кратной размеру блока.

В случае успешного шифрования код возврата — 0 (нуль). Иначе возвращается код ошибки.

Для освобождения памяти и дескриптора используйте функцию gcry_cipher_close.

Эта функция освободит контекст созданный во время выполнения gcry_cipher_open. Так же функция затирает нулями всю уязвимую информацию, которая была создана в рамках дескриптора h.

Процесс де-шифрования

Процесс де-шифрования схож с процессом шифрования функциями, которые необходимо вызвать. А именно (указаны в порядке использования):

  1. gcry_cipher_open — создаем дескриптор контекста
  2. gcry_cipher_setkey — задаем пароль
  3. gcry_cipher_setiv — задаем вектор инициализации
  4. gcry_cipher_decrypt — функция де-шифрования текста
  5. gcry_cipher_close — закрытие дескриптора контекста

Так как все функции (кроме gcry_cipher_decrypt) схожи, рассмотрим только саму функцию де-шифровки.

h — дескриптор контекста
out — буфер куда будет помещен результирующий (расшифрованный) текст
outsize — размер выделенной памяти для буфера out
in — шифрованный текст
inlen — размер шифрованного текста

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

В зависимости от выбранного алгоритма и режима шифрования длина буферов должна быть кратна размеру блока.

В случае успеха функция возвращает 0. Иначе возвращается код ошибки.

Наглядный пример
Компилируем под Linux

gcc -o crypto main.c -lgcrypt

Запускаем

[serge@magnum enc]$ ./crypto -e «This’s my passwd» «It is kinda salt»
Enter text: этот текст необходимо зашифровать
Encryption.
passLength = 16
saltLength = 16
textLength = 65
pass = This’s my passwd
salt = It is kinda salt
text = этот текст необходимо зашифровать
Ecnrypted text = 7DA4C2CB7088BC7432E243B1B1ACAE2A4301CE92D5884404B5AFF181EC4C1B17D3B0565FD82BD88D78916506048BA20E87FA5DDE39288FCC32CA3EF02647F7B140

[serge@magnum enc]$ ./crypto -d «This’s my passwd» «It is kinda salt»
Enter text: 7DA4C2CB7088BC7432E243B1B1ACAE2A4301CE92D5884404B5AFF181EC4C1B17D3B0565FD82BD88D78916506048BA20E87FA5DDE39288FCC32CA3EF02647F7B140
Decryption.
passLength = 16
saltLength = 16
textLength = 65
pass = This’s my passwd
salt = It is kinda salt
text =
Original text = этот текст необходимо зашифровать

Источник

Linux (Command Line)

This article will discuss building Crypto++ from the command line using GNUmakefile on Linux. The Crypto++ makefile and GNU Make is the preferred method of building the library on Unix and Linux. It is preferred over other build systems for several reasons. The most important is, GNU Make and the makefile is the only build system that actually works as expected on the platforms and compilers supported by the library.

The makefile build attempts to follow the GNU Make Book and the GNU Coding Standards. Following the standards means things «just work» as expected for users. Users don’t need to learn a new way to do things using existing toolchains and they don’t need to learn a new build system. We deviate from the instructions on occasion, but the transgressions are usually oversights that we will fix.

The makefile sometimes breaks from tradition due to technical limitations on some platforms. For example, Stallman says the makefile should use the install command during make install . The problem is, it breaks on AIX and Solaris so we have to manually copy files and set permissions.

Читайте также:  D link dsl 200 драйвер для windows 10

The makefile is mostly setup to perform cross-compiles but there are some sharp edges. For example, the makefile uses $CXX -dumpmachine to detect the build target but Clang misreports the build target in some version of the compiler. That starts a cascade of problems as we target the wrong architecture. The problem is discussed below in Cross Compiles.

There is also a specialized makefile setup for Android and iOS cross-compiles in GNUmakefile-cross . GNUmakefile-cross depends upon setenv-xxx.sh scripts. There are several scripts, including setenv-android.sh , setenv-ios.sh and setenv-embedded.sh . setenv-android.sh is mostly broken at this point in time because of Andoid NDK and toolchain changes that occurred around NDK r16.

Some related wiki pages are GNUmakefile and Linux. GNUmakefile attempts to discuss the design of the makefile, and document the various variables and defines used by it. Linux is an early wiki page that holds a lot of information but it is less useful to readers because it covers a lot of material in paragraph bites. BASE+SIMD also discusses files with special needs, and how the makefile hndles the compiler options.

Contents

Building the Library

The library does a good job of configuring itself out of the box. Usually you can perform the following and things work fine:

A typical output from above on an x86_64 machine looks as follows.

If you want to perform dead code stripping, then you should issue make lean instead of make . See Makefile Targets for more information.

In the past you needed to set CRYPTOPP_DATA_DIR to ensure self tests would run after installation, but that is no longer needed since Crypto++ 8.0. See Data Directory for more information.

CXX and CXXFLAGS

The library does its best to honor your configuration choices. For example:

Notice the library uses your CXXFLAGS and then adds other flags to it, like -fPIC and -pipe .

If you are building for OS X or iOS under Xcode, then you probably want to ensure -stdlib=libc++ is present because Xcode uses LLVM’s runtime ( libc++ ). Xcode does not use GNU’s runtime ( libstdc++ ) by default:

You can instruct make to use only your CXXFLAGS by providing them on the command line as an override. Below is a native compile on a LeMaker Banana Pro, and it avoids -pipe because the device does not have the RAM to process some files in-memory:

Testing the Library

You should always test the library after you build it. The library can have trouble on a number of compilers, especially when the optimizer starts inlining functions. Optimizations and bad code generation have been the cause of a number of Crypto++ bug reports. See, for example, Crash on Cygwin-x64 with -DDEBUG -Os and Hang on Debian ARM64 QEMU Chroot.

You test the library using cryptest.exe using both the Validation Suite ( v option) and the Test Vectors ( tv option). Here’s how to invoke it.

If you want a more comprehensive or thorough testing of the library under a compiler on a platform, then run the cryptest.sh script. The cryptest.sh script repeatedly builds the library and executes the self tests under different configurations. More details can be found at Release Process.

Installing the Library

Use the make install recipe to install the library. The recipe will install the header and libraries based on PREFIX . The library will also install test data files when the recipe is executed.

Cross Compiling

Cross-compiles mostly works but there are some sharp edges. When cross-compiling you should set the variables CC , CXX , CXXFLAGS , AR , RANLIB , AS , LD , and LDFLAGS . The makefile uses CXX and CXXFLAGS in combination with -dumpmachine to determine the build target. CXXFLAGS should include —sysroot .

Читайте также:  Windows change appdata path

The table below uses values for Android.mk and NDK r19c. The programs, like the C++ compiler and assembler, should use a full path like /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin .

Cross compile variables

Variable Meaning Example
CC C compiler armv7a-linux-androideabi28-clang
CXX C++ compiler armv7a-linux-androideabi28-clang++
CXXFLAGS C++ compiler flags Include -target armv7-none-linux-androideabi28
AR Archive program arm-linux-androideabi-ar
RANLIB Index program arm-linux-androideabi-ranlib
AS Assembler program arm-linux-androideabi-as
LDFLAGS Linker flags Include -Wl,-target=armv7-none-linux-androideabi28

The library uses the compiler to drive link, so be sure your LDFLAGS includes -Wl so the compiler does not remove options intended for the linker.

Here is one of the sharp edges from Clang and iOS. Notice Clang reports the host architecture of x86_64 , and not the iPhone architecture of armv7-a :

The fix is to invoke make with internal make variables correctly set (other variables omitted for clarity):

User Programs

After building and installing the library you need to compile and link against it. Assuming you installed the library in the default location of /usr/local , then headers are located at /usr/local/include and libraries are located at /usr/local/lib . Your compile command should use -I/usr/local/include and your link command should use -L/usr/local/lib .

Linking correctly can be tricky due to chronic path problems on Linux. You can use one of two strategies. First, use static library linking and avoid runtime problems. Second, use dynamic library linking and additional linker flags. If dynamic linking, then your link command should also include RUNPATH options to ensure you use the correct library at runtime; see Runtime Path for details.

CXX and CXXFLAGS

You should build the library and your programs using the same C++ compiler ( CXX ), the same C++ compiler flags ( CXXFLAGS ), including the same C++ runtime library. Do not mix and match options or runtime libraries.

Runtime libraries tend to be more of a problem on Apple, OS X and iOS platforms. On APple platforms it is usually best to use LLVM’s libc++ . Note that the GNU C++ runtime library is specified with -std=libstdc++ , and the LLVM C++ runtime library is specified with -std=libc++ .

Test Program

The test program used in the examples is shown below. The program multiplies two 64-bit integers and prints the result. The sample program is taken from the Runtime Path wiki article.

Static Linking

The easiest way to sidestep runtime linking problems is to avoid runtime linking. The commands below shows you how to static link.

Or you can compile and link in two separate commands.

Running the program results output similar to the following.

And more importantly, no runtime dependencies on the Crypto++ library.

You can also use -L and -l:libcryptopp.a to achieve the same effect. Also see —library=namespec in the ld(1) man page.

Dynamic Linking

Sometimes you must use dynamic linking, like when two components both depend on Crypto++. That is, your program and another library depend on Crypto++. In this case you should use the shared object to avoid subtle and hard to diagnose memory problems at runtime.

To avoid compiling and linking against one version of a library, and then runtime linking against a different version of a library, you should use a Runtime Path or RUNPATH on Linux. The proper way to use a runtime path is use both -Wl,-R,

and -Wl,—enable-new-dtags . The -Wl is used to pass options through the compiler to the linker. The -R is used from -rpath . -R works on Linux and Solaris, while -rpath does not work on Solaris. You must also use -L during compile.

Running the program results output similar to the following.

Now there is a runtime dependency on the Crypto++ library.

And there is a DT_RUNPATH section present in the ELF headers.

Источник

Оцените статью