Introducing the libgcrypt Encryption Library

Good afternoon, habrahabr!

imageIn the process of writing one of my programs, I needed to deal with the library of encryption and de-encryption of text. I figured it out and now I want to share my experience and knowledge with the community.

This article will focus on the libgcrypt library.

Foreword


I am writing a program under Linux. Therefore, I also searched for the library for this OS. I did not try to find dozens of libraries, then to choose the best. I chose the one that suited my needs - one that is used in fairly well-known products - two.

About libgcrypt library itself


The library provides a high-level interface to low-level cryptography mechanisms. Simply put, you choose the encryption mechanism you need, come up with a password and encode the text without delving into how your chosen algorithm works.
The library was written as part of the GnuPG project and is distributed under the LGPL license.

Encryption process


In the encryption process, we will use the following functions (indicated in the order of use):
  1. gcry_cipher_open - create a context handle
  2. gcry_cipher_setkey - set a password
  3. gcry_cipher_setiv - set the initialization vector
  4. gcry_cipher_encrypt - text encryption function
  5. gcry_cipher_close - closing the context descriptor

Now more about each function.

gcry_error_t gcry_cipher_open (gcry_cipher_hd_t *hd, int algo, int mode, unsigned int flags)

The function creates a context descriptor, which is needed for further encryption functions, and returns the descriptor in 'hd'. In case of an error, a nonzero error code is returned.

hd is a pointer to our future context descriptor.

algo is an algorithm that we are going to use to encrypt text. Examples:
GCRY_CIPHER_IDEA - IDEA algorithm. Although it can be chosen, it will not work. Since the algorithm is patented, there is no implementation for it in a free library.
GCRY_CIPHER_3DES - (Triple-DES with 3 Keys as EDE) symmetric block cipher.
GCRY_CIPHER_BLOWFISH - Blowfish algorithm. The current implementation allows using only a 128-bit key.
There are also RIJNDAEL, TWOFISH, AES, SERPENT and so on.
(The entire list of algorithms can be found here )

mode - one of the following options:
  • GCRY_CIPHER_MODE_NONE - do not use any modifier. (Avoid using this key.)
  • GCRY_CIPHER_MODE_ECB - electronic codebook mode. ( Electronic Code Book )
  • GCRY_CIPHER_MODE_CFB - ciphertext feedback mode. ( Cipher FeedBack )
  • GCRY_CIPHER_MODE_CBC - mode of coupling blocks of ciphertext. ( Cipher Block Chaining )
  • GCRY_CIPHER_MODE_STREAM - mode for use only with stream algorithms. For example, GCRY_CIPHER_MODE_STREAM.
  • GCRY_CIPHER_MODE_OFB - output feedback mode. ( Output FeedBack )
  • GCRY_CIPHER_MODE_CTR - counter mode. ( Counter )

flags - can be 0 (zero), or a combination of the following flags:
  • GCRY_CIPHER_SECURE - all operations are located in protected memory.
  • GCRY_CIPHER_ENABLE_SYNC - this flag enables CFB synchronization mode.
  • GCRY_CIPHER_CBC_CTS - enables CTS (cipher text stealing) for CBC mode. (Flan cannot be used simultaneously with GCRY_CIPHER_CBC_MAC.) CTS mode allows you to make data changes of arbitrary size.
  • GCRY_CIPHER_CBC_MAC - calculate CBC MAC keyed checksum. (A flan cannot be used at the same time as GCRY_CIPHER_CBC_CTS.)

Example

gcryError = gcry_cipher_open(
	        &gcryCipherHd,
	        GCRY_CIPHER_AES128,
	        GCRY_CIPHER_MODE_CBC,
	        GCRY_CIPHER_CBC_CTS);

In order to continue working with the descriptor, first of all, we need to set the key using the gcry_cipher_setkey function.

gcry_error_t gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *k, size_t l)

hd - the descriptor received earlier
k is the key, it is also the password (character string)
l is the key length (strlen (k))

Most encryption modes require an initialization vector, which is usually a non-secret random string that acts as a salt . In the case of CTR mode, you must specify a counter that is also similar to the value of “salt”. To set these values ​​we use functions:
  • gcry_cipher_setiv (gcry_cipher_hd_t h, const void * k, size_t l)
    Set the initialization vector used for encryption or de-encryption. The vector is transmitted as a buffer k of length l bytes and is copied to the internal data structure. The function also checks whether the vector meets the necessary requirements for a given algorithm (algo) and mode (mode).
  • gcry_cipher_setctr (gcry_cipher_hd_t h, const void * c, size_t l)
    Set the wind counter used for encryption or de-encryption. The counter is transmitted as a buffer k of length l bytes and is copied to the internal data structure. The function also checks whether the vector meets the necessary requirements for the given algorithm (i.e. the vector must be the same size as the block size).

So we come to the key point - encryption. The encryption process itself is performed by the gcry_cipher_encrypt function.

gcry_error_t gcry_cipher_encrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)

The function can work with either one or two buffers. If the in value is passed as NULL and inlen is 0 (zero), then encryption with one buffer is performed. Simply put, the out buffer, in which, before calling the function, contains non-encrypted text, when exiting the function, it will be rewritten with new text, encrypted. If the in value is passed as non-NULL, then the inlen byte is encrypted and placed in the out buffer (which must be at least the size of inlen). outsize should display the size of the allocated piece of memory for the out buffer so that the function can check if there is enough space for output. (Overlapping buffers is not permitted.)

Depending on the selected algorithm and encryption mode, the length of the buffers must be a multiple of the block size.

In case of successful encryption, the return code is 0 (zero). Otherwise, an error code is returned.

To free memory and handle use the gcry_cipher_close function.

void gcry_cipher_close (gcry_cipher_hd_t h)

This function will free the context created at runtime by gcry_cipher_open. Also, the function zeros all vulnerable information that was created within the h descriptor.

De encryption process


The de-encryption process is similar to the encryption process of the functions you need to call. Namely (indicated in the order of use):
  1. gcry_cipher_open - create a context handle
  2. gcry_cipher_setkey - set a password
  3. gcry_cipher_setiv - set the initialization vector
  4. gcry_cipher_decrypt - text de-encryption function
  5. gcry_cipher_close - closing the context descriptor

Since all functions (except gcry_cipher_decrypt) are similar, we consider only the de-encryption function itself.

gcry_error_t gcry_cipher_decrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)

h - context descriptor
out - buffer where the resulting (decrypted) text will be placed
outsize - size of the allocated memory for the buffer out
in - encrypted text
inlen - size of encrypted text

Like the encryption function, the decryption function can do with one buffer. To do this, pass zeros instead of in and inlen. If these parameters are not zero, then the inlen byte is decrypted and put in the out buffer, which should be at least equal to inlen. outsize must be set to the number of bytes allocated for the out buffer so that the function can verify that there is enough space for the result. (Overlapping with a buffer is not allowed.)

Depending on the chosen algorithm and encryption mode, the length of the buffers should be a multiple of the block size.

If successful, the function returns 0. Otherwise, an error code is returned.

Good example

#include 
#include 
#define ENCR 1
#define DECR 0
void myCrypt(int encdec, const char * pass, const char * salt, const char * text) {
  gcry_error_t     gcryError;
  gcry_cipher_hd_t hd;
  size_t           i;
  size_t passLength = strlen(pass);
  size_t saltLength = strlen(salt);
  size_t textLength = strlen(text)+encdec;
  char  * outBuffer = (char*)malloc(textLength);
  printf("%scryption...\n", encdec?"En":"De");
  printf("passLength = %d\n", passLength);
  printf("saltLength = %d\n", saltLength);
  printf("textLength = %d\n", textLength);
  printf("      pass = %s\n", pass);
  printf("      salt = %s\n", salt);
  printf("      text = %s\n", encdec?text:"");
  // используем алгоритм шифрования - GCRY_CIPHER_AES128
  // используем режим сцепления блоков шифротекста
  // используем флаг GCRY_CIPHER_CBC_CTS, чтобы можно было шифровать текст любой длины
  gcryError = gcry_cipher_open(&hd, 
			       GCRY_CIPHER_AES128, 
			       GCRY_CIPHER_MODE_CBC, 
			       GCRY_CIPHER_CBC_CTS);
  if (gcryError) {
      printf("gcry_cipher_open failed:  %s/%s\n", 
	     gcry_strsource(gcryError), gcry_strerror(gcryError));
      return;
  }
  gcryError = gcry_cipher_setkey(hd, pass, passLength);
  if (gcryError) {
      printf("gcry_cipher_setkey failed:  %s/%s\n", 
	     gcry_strsource(gcryError), gcry_strerror(gcryError));
      return;
  }
  gcryError = gcry_cipher_setiv(hd, salt, saltLength);
  if (gcryError) {
      printf("gcry_cipher_setiv failed:  %s/%s\n", 
	     gcry_strsource(gcryError),gcry_strerror(gcryError));
      return;
  }
  switch (encdec) {
    case ENCR:
      gcryError = gcry_cipher_encrypt(hd, outBuffer, textLength, text, textLength);
      break;
    case DECR:
      gcryError = gcry_cipher_decrypt(hd, outBuffer, textLength, text, textLength);
  }
  if (gcryError) {
      printf("gcry_cipher_encrypt failed:  %s/%s\n", 
	     gcry_strsource(gcryError), gcry_strerror(gcryError));
      return;
  }
  switch (encdec) {
    case ENCR:
      printf("Ecnrypted text = ");
      for (i = 0; i \"\" \"\"\n", argv[0]);
    return 1;
  }
  int encdec = ENCR;
  char line[1024];
  printf("Enter text: ");
  fgets(line, sizeof(line), stdin);
  if ( !strcmp(argv[1], "-d") ) {
    // Преобразуем строку из 16чного представления в символьный
    int i = 0; char a[3] = {"00"};
    for (; i

Компилируем под 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 = этот текст необходимо зашифровать


Более детальное описание библиотеки и интерфейсных функций можно найти по адресу: http://www.gnupg.org/documentation/manuals/gcrypt

Also popular now: